diff --git a/pom.xml b/pom.xml index 6a88164..6e0edcc 100644 --- a/pom.xml +++ b/pom.xml @@ -34,6 +34,7 @@ + org.springframework.boot spring-boot-starter-security @@ -105,11 +106,11 @@ lombok true - - - - - + + org.springframework.boot + spring-boot-devtools + runtime + org.springframework.boot spring-boot-starter-data-jpa @@ -170,11 +171,6 @@ - - org.mockito - mockito-core - test - diff --git a/src/main/java/com/nhnacademy/bookstorecoupon/couponpolicy/controller/CouponPolicyController.java b/src/main/java/com/nhnacademy/bookstorecoupon/couponpolicy/controller/CouponPolicyController.java index a6b71bb..a6d9a21 100644 --- a/src/main/java/com/nhnacademy/bookstorecoupon/couponpolicy/controller/CouponPolicyController.java +++ b/src/main/java/com/nhnacademy/bookstorecoupon/couponpolicy/controller/CouponPolicyController.java @@ -40,11 +40,11 @@ public CouponPolicyController(CouponPolicyService couponPolicyService) { } @Operation( - summary = "웰컴쿠폰 생성", - description = "웰컴쿠폰을 생성합니다" + summary = "웰컴쿠폰정책 생성", + description = "웰컴쿠폰정책을 생성합니다" ) @ApiResponses(value = { - @ApiResponse(responseCode = "201", description = "웰컴쿠폰이 성공적으로 발행되었습니다."), + @ApiResponse(responseCode = "201", description = "웰컴쿠폰정책이 성공적으로 발행되었습니다."), @ApiResponse(responseCode = "400", description = "잘못된 요청입니다.") }) @AuthorizeRole({"COUPON_ADMIN", "HEAD_ADMIN"}) @@ -56,11 +56,11 @@ public ResponseEntity issueWelcomeCoupon( return ResponseEntity.status(HttpStatus.CREATED).build(); } @Operation( - summary = "생일쿠폰 생성", - description = "웰컴쿠폰을 생성합니다" + summary = "생일쿠폰정책 생성", + description = "생일쿠폰정책을 생성합니다" ) @ApiResponses(value = { - @ApiResponse(responseCode = "201", description = "생일쿠폰이 성공적으로 발행되었습니다."), + @ApiResponse(responseCode = "201", description = "생일쿠폰정책이 성공적으로 발행되었습니다."), @ApiResponse(responseCode = "400", description = "잘못된 요청입니다.") }) @AuthorizeRole({"COUPON_ADMIN", "HEAD_ADMIN"}) diff --git a/src/main/java/com/nhnacademy/bookstorecoupon/couponpolicy/exception/CouponPolicyBanUpdateException.java b/src/main/java/com/nhnacademy/bookstorecoupon/couponpolicy/exception/CouponPolicyBanUpdateException.java deleted file mode 100644 index 7d76e96..0000000 --- a/src/main/java/com/nhnacademy/bookstorecoupon/couponpolicy/exception/CouponPolicyBanUpdateException.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.nhnacademy.bookstorecoupon.couponpolicy.exception; - - -import com.nhnacademy.bookstorecoupon.global.exception.GlobalException; -import com.nhnacademy.bookstorecoupon.global.exception.payload.ErrorStatus; - -import lombok.Getter; - -@Getter -public class CouponPolicyBanUpdateException extends GlobalException { - public CouponPolicyBanUpdateException(ErrorStatus errorStatus) { - super(errorStatus); - } -} diff --git a/src/main/java/com/nhnacademy/bookstorecoupon/couponpolicy/repository/CustomCouponPolicyRepository.java b/src/main/java/com/nhnacademy/bookstorecoupon/couponpolicy/repository/CustomCouponPolicyRepository.java index 045f78c..6f0c28a 100644 --- a/src/main/java/com/nhnacademy/bookstorecoupon/couponpolicy/repository/CustomCouponPolicyRepository.java +++ b/src/main/java/com/nhnacademy/bookstorecoupon/couponpolicy/repository/CustomCouponPolicyRepository.java @@ -14,5 +14,4 @@ public interface CustomCouponPolicyRepository { Page findAllWithBooksAndCategories(Pageable pageable, Map bookIdMap, Map categoryIdMap); Optional findLatestCouponPolicyByType(String type); - } diff --git a/src/main/java/com/nhnacademy/bookstorecoupon/couponpolicy/repository/impl/CustomCouponPolicyRepositoryImpl.java b/src/main/java/com/nhnacademy/bookstorecoupon/couponpolicy/repository/impl/CustomCouponPolicyRepositoryImpl.java index e08f308..a6955ae 100644 --- a/src/main/java/com/nhnacademy/bookstorecoupon/couponpolicy/repository/impl/CustomCouponPolicyRepositoryImpl.java +++ b/src/main/java/com/nhnacademy/bookstorecoupon/couponpolicy/repository/impl/CustomCouponPolicyRepositoryImpl.java @@ -29,10 +29,6 @@ public Page findAllWithBooksAndCategories(Pageable page Map categoryIdMap) { QCouponPolicy couponPolicy = QCouponPolicy.couponPolicy; - - - - // Fetch coupon policies with pagination and ordering List couponPolicies = queryFactory .selectFrom(couponPolicy) diff --git a/src/main/java/com/nhnacademy/bookstorecoupon/coupontemplate/controller/CouponTemplateController.java b/src/main/java/com/nhnacademy/bookstorecoupon/coupontemplate/controller/CouponTemplateController.java index a78c479..78ec429 100644 --- a/src/main/java/com/nhnacademy/bookstorecoupon/coupontemplate/controller/CouponTemplateController.java +++ b/src/main/java/com/nhnacademy/bookstorecoupon/coupontemplate/controller/CouponTemplateController.java @@ -36,6 +36,8 @@ public CouponTemplateController(CouponTemplateService couponTemplateService) { } + + @Operation(summary = "쿠폰 템플릿 생성", description = "새로운 쿠폰 템플릿을 생성합니다.") @ApiResponses(value = { @ApiResponse(responseCode = "201", description = "쿠폰 템플릿이 성공적으로 생성되었습니다."), diff --git a/src/main/java/com/nhnacademy/bookstorecoupon/global/config/SecurityConfig.java b/src/main/java/com/nhnacademy/bookstorecoupon/global/config/SecurityConfig.java index 492df3b..43d64e3 100644 --- a/src/main/java/com/nhnacademy/bookstorecoupon/global/config/SecurityConfig.java +++ b/src/main/java/com/nhnacademy/bookstorecoupon/global/config/SecurityConfig.java @@ -43,7 +43,6 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .anyRequest().permitAll() ) .addFilterBefore(ipAddressFilter, UsernamePasswordAuthenticationFilter.class) - // .addFilterAfter(new JwtFilter(jwtUtils, tokenReissueClient), IpAddressFilter.class) .addFilterBefore(new JwtFilter(jwtUtils, tokenReissueClient, accessTokenExpiresIn, refreshTokenExpiresIn), UsernamePasswordAuthenticationFilter.class) .sessionManagement((session) -> session diff --git a/src/main/java/com/nhnacademy/bookstorecoupon/global/controller/SwaggerControllerTest.java b/src/main/java/com/nhnacademy/bookstorecoupon/global/controller/SwaggerControllerTest.java deleted file mode 100644 index c442d6f..0000000 --- a/src/main/java/com/nhnacademy/bookstorecoupon/global/controller/SwaggerControllerTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.nhnacademy.bookstorecoupon.global.controller; - -import java.nio.charset.StandardCharsets; -import java.util.Locale; - -import org.springdoc.webmvc.api.OpenApiResource; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; - -import com.fasterxml.jackson.core.JsonProcessingException; - -import jakarta.servlet.http.HttpServletRequest; -import lombok.extern.slf4j.Slf4j; - -@Controller -@RequestMapping("/coupons/api-test") -@Slf4j -public class SwaggerControllerTest { - - @Autowired - private OpenApiResource openApiResource; - - @GetMapping() - public String getSwaggerJson(HttpServletRequest request, Model model) throws JsonProcessingException { - String apiDocsUrl = "/coupon-docs/coupon-api"; // Swagger 문서 URL - Locale locale = request.getLocale(); // 현재 요청의 Locale - - byte[] swaggerJsonBytes = openApiResource.openapiJson(request, apiDocsUrl, locale); - if (swaggerJsonBytes == null) { - model.addAttribute("swaggerJson", "{}"); - } else { - String swaggerJson = new String(swaggerJsonBytes, StandardCharsets.UTF_8); - model.addAttribute("swaggerJson", swaggerJson); - } - - return "api/coupon-api"; - } -} \ No newline at end of file diff --git a/src/main/java/com/nhnacademy/bookstorecoupon/global/handler/GlobalExceptionHandler.java b/src/main/java/com/nhnacademy/bookstorecoupon/global/handler/GlobalExceptionHandler.java index 44e1f5c..6ed4226 100644 --- a/src/main/java/com/nhnacademy/bookstorecoupon/global/handler/GlobalExceptionHandler.java +++ b/src/main/java/com/nhnacademy/bookstorecoupon/global/handler/GlobalExceptionHandler.java @@ -13,16 +13,13 @@ import com.nhnacademy.bookstorecoupon.global.exception.GlobalException; import com.nhnacademy.bookstorecoupon.global.exception.payload.ErrorStatus; +import lombok.extern.slf4j.Slf4j; + +@Slf4j @RestControllerAdvice public class GlobalExceptionHandler { - @ExceptionHandler({GlobalException.class}) - public ResponseEntity handleExceptionGlobally(GlobalException ex) { - ErrorStatus errorStatus=ex.getErrorStatus(); - return new ResponseEntity<>(errorStatus, errorStatus.getStatus()); - } - @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity processValidationError(MethodArgumentNotValidException exception) { BindingResult bindingResult = exception.getBindingResult(); @@ -47,8 +44,29 @@ public ResponseEntity processValidationError(MethodArgumentNotValid LocalDateTime.now() ); + log.error("validation 에러: {}", exception.getMessage()); return new ResponseEntity<>(errorStatus, HttpStatus.BAD_REQUEST); } + + + @ExceptionHandler({GlobalException.class}) + public ResponseEntity handleGlobalException(GlobalException ex) { + ErrorStatus errorStatus=ex.getErrorStatus(); + + log.error("글로벌 에러: {}", ex.getMessage()); + return new ResponseEntity<>(errorStatus, errorStatus.getStatus()); + } + + + + @ExceptionHandler({Exception.class}) + public ResponseEntity handleException(Exception ex) { + + + log.error("서버에러: {}", ex.getMessage()); + return ResponseEntity.status(500).build(); + } + } diff --git a/src/main/java/com/nhnacademy/bookstorecoupon/userandcoupon/service/impl/UserAndCouponServiceImpl.java b/src/main/java/com/nhnacademy/bookstorecoupon/userandcoupon/service/impl/UserAndCouponServiceImpl.java index 80f485c..7c58c19 100644 --- a/src/main/java/com/nhnacademy/bookstorecoupon/userandcoupon/service/impl/UserAndCouponServiceImpl.java +++ b/src/main/java/com/nhnacademy/bookstorecoupon/userandcoupon/service/impl/UserAndCouponServiceImpl.java @@ -112,8 +112,6 @@ public void issueBirthdayCoupon() { .orElseThrow(() -> new CouponPolicyNotFoundException(ErrorStatus.from("최신 생일쿠폰 정책을 찾을 수 없습니다.", HttpStatus.NOT_FOUND, LocalDateTime.now()))); // 생일 목록에 있는 각 사용자에게 쿠폰을 발행 - - birthdayList.forEach(user -> { UserAndCoupon userAndCoupon = UserAndCoupon.builder() diff --git a/src/test/java/com/nhnacademy/bookstorecoupon/auth/jwt/filter/JwtFilterTest.java b/src/test/java/com/nhnacademy/bookstorecoupon/auth/jwt/filter/JwtFilterTest.java new file mode 100644 index 0000000..9dc1f20 --- /dev/null +++ b/src/test/java/com/nhnacademy/bookstorecoupon/auth/jwt/filter/JwtFilterTest.java @@ -0,0 +1,140 @@ +package com.nhnacademy.bookstorecoupon.auth.jwt.filter; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Collections; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import com.nhnacademy.bookstorecoupon.auth.jwt.client.TokenReissueClient; +import com.nhnacademy.bookstorecoupon.auth.jwt.dto.request.ReissueTokenRequest; +import com.nhnacademy.bookstorecoupon.auth.jwt.dto.response.ReissueTokensResponse; +import com.nhnacademy.bookstorecoupon.auth.jwt.utils.JwtUtils; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +class JwtFilterTest { + + + @InjectMocks + private JwtFilter jwtFilter; + + @Mock + private JwtUtils jwtUtils; + + @Mock + private TokenReissueClient tokenReissueClient; + + @Mock + private HttpServletRequest request; + + @Mock + private HttpServletResponse response; + + @Mock + private FilterChain filterChain; + + @Mock + private PrintWriter writer; // Add a mock for PrintWriter + + private static final String VALID_ACCESS_TOKEN = "valid-access-token"; + private static final String EXPIRED_ACCESS_TOKEN = "expired-access-token"; + private static final String VALID_REFRESH_TOKEN = "valid-refresh-token"; + private static final String NEW_ACCESS_TOKEN = "new-access-token"; + private static final String NEW_REFRESH_TOKEN = "new-refresh-token"; + + @BeforeEach + void setup() { + MockitoAnnotations.openMocks(this); + try { + when(response.getWriter()).thenReturn(writer); // Mock PrintWriter + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Test + void testDoFilterInternal_ValidToken() throws Exception { + when(request.getHeader("Authorization")).thenReturn(VALID_ACCESS_TOKEN); + when(request.getHeader("Refresh-Token")).thenReturn(VALID_REFRESH_TOKEN); + when(jwtUtils.validateToken(VALID_ACCESS_TOKEN)).thenReturn(null); + when(jwtUtils.getUserIdFromToken(VALID_ACCESS_TOKEN)).thenReturn(123L); + when(jwtUtils.getUserRolesFromToken(VALID_ACCESS_TOKEN)).thenReturn(Collections.singletonList("ROLE_USER")); + + jwtFilter.doFilterInternal(request, response, filterChain); + + verify(filterChain).doFilter(request, response); + verifyNoMoreInteractions(response); + } + + @Test + void testDoFilterInternal_ExpiredToken() throws Exception { + when(request.getHeader("Authorization")).thenReturn(EXPIRED_ACCESS_TOKEN); + when(request.getHeader("Refresh-Token")).thenReturn(VALID_REFRESH_TOKEN); + when(jwtUtils.validateToken(EXPIRED_ACCESS_TOKEN)).thenReturn("만료된 토큰입니다."); + when(tokenReissueClient.reissueTokensWithRefreshToken(any(ReissueTokenRequest.class))) + .thenReturn(ResponseEntity.ok(new ReissueTokensResponse(NEW_ACCESS_TOKEN, NEW_REFRESH_TOKEN))); + + jwtFilter.doFilterInternal(request, response, filterChain); + + ArgumentCaptor authHeaderCaptor = ArgumentCaptor.forClass(String.class); + ArgumentCaptor refreshHeaderCaptor = ArgumentCaptor.forClass(String.class); + verify(response).setHeader(eq("New-Authorization"), authHeaderCaptor.capture()); + verify(response).setHeader(eq("New-Refresh-Token"), refreshHeaderCaptor.capture()); + + assertEquals(NEW_ACCESS_TOKEN, authHeaderCaptor.getValue()); + assertEquals(NEW_REFRESH_TOKEN, refreshHeaderCaptor.getValue()); + verify(filterChain).doFilter(request, response); + } + + @Test + void testDoFilterInternal_TokenReissueFailure() throws Exception { + when(request.getHeader("Authorization")).thenReturn(EXPIRED_ACCESS_TOKEN); + when(request.getHeader("Refresh-Token")).thenReturn(VALID_REFRESH_TOKEN); + when(jwtUtils.validateToken(EXPIRED_ACCESS_TOKEN)).thenReturn("만료된 토큰입니다."); + when(tokenReissueClient.reissueTokensWithRefreshToken(any(ReissueTokenRequest.class))) + .thenReturn(ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null)); + + jwtFilter.doFilterInternal(request, response, filterChain); + + verify(response).setStatus(HttpServletResponse.SC_UNAUTHORIZED); + verify(writer).print("토큰 재발급에 실패했습니다. 다시 로그인해주세요."); // Mock PrintWriter behavior + verifyNoMoreInteractions(filterChain); + } + + @Test + void testDoFilterInternal_InvalidToken() throws Exception { + when(request.getHeader("Authorization")).thenReturn(VALID_ACCESS_TOKEN); + when(request.getHeader("Refresh-Token")).thenReturn(VALID_REFRESH_TOKEN); + when(jwtUtils.validateToken(VALID_ACCESS_TOKEN)).thenReturn("유효하지 않은 토큰입니다."); + + jwtFilter.doFilterInternal(request, response, filterChain); + + verify(response).setStatus(HttpServletResponse.SC_UNAUTHORIZED); + verify(writer).print("유효하지 않은 토큰입니다."); // Mock PrintWriter behavior + verifyNoMoreInteractions(filterChain); + } + + @Test + void testDoFilterInternal_NoTokenProvided() throws Exception { + when(request.getHeader("Authorization")).thenReturn(null); + + jwtFilter.doFilterInternal(request, response, filterChain); + + verify(filterChain).doFilter(request, response); + verifyNoMoreInteractions(response); + } + +} \ No newline at end of file diff --git a/src/test/java/com/nhnacademy/bookstorecoupon/auth/jwt/utils/JwtUtilsTest.java b/src/test/java/com/nhnacademy/bookstorecoupon/auth/jwt/utils/JwtUtilsTest.java new file mode 100644 index 0000000..de8607a --- /dev/null +++ b/src/test/java/com/nhnacademy/bookstorecoupon/auth/jwt/utils/JwtUtilsTest.java @@ -0,0 +1,96 @@ +package com.nhnacademy.bookstorecoupon.auth.jwt.utils; + +import static org.junit.jupiter.api.Assertions.*; + +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +import javax.crypto.SecretKey; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.security.Keys; + +@SpringBootTest +class JwtUtilsTest { + + private JwtUtils jwtUtils; + private SecretKey secretKey; + + private static final String SECRET = "supersecretkeythatshouldbeatleast32characterslong"; + + @BeforeEach + void setup() { + secretKey = Keys.hmacShaKeyFor(SECRET.getBytes(StandardCharsets.UTF_8)); + jwtUtils = new JwtUtils(SECRET); + } + + @Test + void testGetUserIdFromToken() { + Long expectedUserId = 123L; + + String token = Jwts.builder() + .claim("userId", expectedUserId) + .signWith(secretKey, SignatureAlgorithm.HS256) + .compact(); + + Long userId = jwtUtils.getUserIdFromToken("Bearer " + token); + assertEquals(expectedUserId, userId); + } + + @Test + void testGetUserRolesFromToken() { + List expectedRoles = Collections.singletonList("ROLE_USER"); + + String token = Jwts.builder() + .claim("roles", expectedRoles) + .signWith(secretKey, SignatureAlgorithm.HS256) + .compact(); + + List roles = jwtUtils.getUserRolesFromToken("Bearer " + token); + assertEquals(expectedRoles, roles); + } + + @Test + void testValidateToken_ValidToken() { + String token = Jwts.builder() + .claim("userId", 123L) + .signWith(secretKey, SignatureAlgorithm.HS256) + .compact(); + + String errorMessage = jwtUtils.validateToken("Bearer " + token); + assertNull(errorMessage); + } + + @Test + void testValidateToken_ExpiredToken() { + String token = Jwts.builder() + .claim("userId", 123L) + .setExpiration(new Date(System.currentTimeMillis() - 1000)) // Expired + .signWith(secretKey, SignatureAlgorithm.HS256) + .compact(); + + String errorMessage = jwtUtils.validateToken("Bearer " + token); + assertEquals("만료된 토큰입니다.", errorMessage); + } + + @Test + void testValidateToken_InvalidToken() { + String token = "Bearer invalid_token"; + + String errorMessage = jwtUtils.validateToken(token); + assertEquals("유효하지 않은 토큰입니다.", errorMessage); + } + + @Test + void testValidateToken_NullToken() { + String errorMessage = jwtUtils.validateToken("Bearer "); + assertEquals("토큰 값이 비어있습니다.", errorMessage); + } +} \ No newline at end of file diff --git a/src/test/java/com/nhnacademy/bookstorecoupon/bookcoupon/domain/entity/BookCouponTest.java b/src/test/java/com/nhnacademy/bookstorecoupon/bookcoupon/domain/entity/BookCouponTest.java index b9cdc3d..5bcde42 100644 --- a/src/test/java/com/nhnacademy/bookstorecoupon/bookcoupon/domain/entity/BookCouponTest.java +++ b/src/test/java/com/nhnacademy/bookstorecoupon/bookcoupon/domain/entity/BookCouponTest.java @@ -9,6 +9,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; import com.nhnacademy.bookstorecoupon.bookcoupon.repository.BookCouponRepository; import com.nhnacademy.bookstorecoupon.couponpolicy.domain.entity.CouponPolicy; @@ -17,6 +18,7 @@ @DataJpaTest @Import(QuerydslConfig.class) +@ActiveProfiles("test") class BookCouponTest { @Autowired diff --git a/src/test/java/com/nhnacademy/bookstorecoupon/bookcoupon/repository/impl/CustomBookCouponRepositoryImplTest.java b/src/test/java/com/nhnacademy/bookstorecoupon/bookcoupon/repository/impl/CustomBookCouponRepositoryImplTest.java new file mode 100644 index 0000000..45bf330 --- /dev/null +++ b/src/test/java/com/nhnacademy/bookstorecoupon/bookcoupon/repository/impl/CustomBookCouponRepositoryImplTest.java @@ -0,0 +1,88 @@ +package com.nhnacademy.bookstorecoupon.bookcoupon.repository.impl; + +import static org.assertj.core.api.Assertions.*; + +import java.math.BigDecimal; +import java.util.Map; + +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.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +import com.nhnacademy.bookstorecoupon.bookcoupon.domain.entity.BookCoupon; +import com.nhnacademy.bookstorecoupon.config.QuerydslTestConfig; +import com.nhnacademy.bookstorecoupon.couponpolicy.domain.entity.CouponPolicy; +import com.querydsl.jpa.impl.JPAQueryFactory; + +import jakarta.persistence.EntityManager; + +@DataJpaTest +@Import(QuerydslTestConfig.class) // QueryDSL 설정 파일 임포트 +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) // 실제 데이터베이스 연결 +public class CustomBookCouponRepositoryImplTest { + + @Autowired + private EntityManager entityManager; + + @Autowired + private JPAQueryFactory jpaQueryFactory; + + private CustomBookCouponRepositoryImpl customBookCouponRepository; + + @BeforeEach + void setUp() { + customBookCouponRepository = new CustomBookCouponRepositoryImpl(jpaQueryFactory); + + // Create CouponPolicy + CouponPolicy couponPolicy = CouponPolicy.builder() + .minOrderPrice(BigDecimal.valueOf(1000)) + .salePrice(BigDecimal.valueOf(100)) + .saleRate(null) + .maxSalePrice(null) + .type("book") + .build(); + entityManager.persist(couponPolicy); + + // Create BookCoupons + BookCoupon bookCoupon1 = BookCoupon.builder() + .couponPolicy(couponPolicy) + .bookId(123L) + .bookTitle("Sample Book 1") + .build(); + entityManager.persist(bookCoupon1); + + + + // Flush and clear to ensure persistence context is updated + entityManager.flush(); + entityManager.clear(); + } + + + + + + @Test + void testFetchBookIdMap() { + + + // When + Map bookIdMap = customBookCouponRepository.fetchBookIdMap(); + + // Then + assertThat(bookIdMap).hasSize(1); // Expecting one BookCoupon entry + + // Verify the specific book ID + assertThat(bookIdMap).containsKey(1L); // The key should be 123L + + // Verify the details of the BookInfo + BookCoupon.BookInfo expectedBookInfo = new BookCoupon.BookInfo(123L, "Sample Book 1"); + BookCoupon.BookInfo actualBookInfo = bookIdMap.get(1L); + + assertThat(actualBookInfo.bookId).isEqualTo(expectedBookInfo.bookId); + assertThat(actualBookInfo.bookTitle).isEqualTo(expectedBookInfo.bookTitle); + } +} \ No newline at end of file diff --git a/src/test/java/com/nhnacademy/bookstorecoupon/categorycoupon/domain/entity/CategoryCouponTest.java b/src/test/java/com/nhnacademy/bookstorecoupon/categorycoupon/domain/entity/CategoryCouponTest.java index f62fb4f..789e319 100644 --- a/src/test/java/com/nhnacademy/bookstorecoupon/categorycoupon/domain/entity/CategoryCouponTest.java +++ b/src/test/java/com/nhnacademy/bookstorecoupon/categorycoupon/domain/entity/CategoryCouponTest.java @@ -9,6 +9,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; import com.nhnacademy.bookstorecoupon.categorycoupon.repository.CategoryCouponRepository; import com.nhnacademy.bookstorecoupon.couponpolicy.domain.entity.CouponPolicy; @@ -17,6 +18,7 @@ @DataJpaTest @Import(QuerydslConfig.class) +@ActiveProfiles("test") class CategoryCouponTest { @Autowired diff --git a/src/test/java/com/nhnacademy/bookstorecoupon/categorycoupon/repository/impl/CustomCategoryCouponRepositoryImplTest.java b/src/test/java/com/nhnacademy/bookstorecoupon/categorycoupon/repository/impl/CustomCategoryCouponRepositoryImplTest.java new file mode 100644 index 0000000..49d7817 --- /dev/null +++ b/src/test/java/com/nhnacademy/bookstorecoupon/categorycoupon/repository/impl/CustomCategoryCouponRepositoryImplTest.java @@ -0,0 +1,90 @@ +package com.nhnacademy.bookstorecoupon.categorycoupon.repository.impl; + +import static org.assertj.core.api.Assertions.*; + +import java.math.BigDecimal; +import java.util.Map; + +import org.junit.jupiter.api.AfterEach; +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.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +import com.nhnacademy.bookstorecoupon.categorycoupon.domain.entity.CategoryCoupon; +import com.nhnacademy.bookstorecoupon.config.QuerydslTestConfig; +import com.nhnacademy.bookstorecoupon.couponpolicy.domain.entity.CouponPolicy; +import com.querydsl.jpa.impl.JPAQueryFactory; + +import jakarta.persistence.EntityManager; + +@DataJpaTest +@Import(QuerydslTestConfig.class) +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +class CustomCategoryCouponRepositoryImplTest { + + @Autowired + private EntityManager entityManager; + + @Autowired + private JPAQueryFactory jpaQueryFactory; + + private CustomCategoryCouponRepositoryImpl customCategoryCouponRepository; + + @BeforeEach + void setUp() { + customCategoryCouponRepository = new CustomCategoryCouponRepositoryImpl(jpaQueryFactory); + + // Create CouponPolicy + CouponPolicy couponPolicy = CouponPolicy.builder() + .minOrderPrice(BigDecimal.valueOf(1000)) + .salePrice(BigDecimal.valueOf(100)) + .saleRate(null) + .maxSalePrice(null) + .type("category") + .build(); + entityManager.persist(couponPolicy); + + // Create CategoryCoupons + CategoryCoupon categoryCoupon1 = CategoryCoupon.builder() + .couponPolicy(couponPolicy) + .categoryId(456L) + .categoryName("Sample Category 1") + .build(); + entityManager.persist(categoryCoupon1); + + // Flush and clear to ensure persistence context is updated + entityManager.flush(); + entityManager.clear(); + } + + @AfterEach + void resetAutoIncrementId() { + + entityManager.createQuery("DELETE FROM CategoryCoupon").executeUpdate(); + entityManager.createQuery("DELETE FROM CouponPolicy").executeUpdate(); + } + + + + @Test + void testFetchCategoryIdMap() { + // When + Map categoryIdMap = customCategoryCouponRepository.fetchCategoryIdMap(); + + // Then + assertThat(categoryIdMap).hasSize(1); // Expecting one CategoryCoupon entry + + // Verify the specific category ID + assertThat(categoryIdMap).containsKey(1L); // The key should be 456L + + // Verify the details of the CategoryInfo + CategoryCoupon.CategoryInfo expectedCategoryInfo = new CategoryCoupon.CategoryInfo(456L, "Sample Category 1"); + CategoryCoupon.CategoryInfo actualCategoryInfo = categoryIdMap.get(1L); + + assertThat(actualCategoryInfo.categoryId).isEqualTo(expectedCategoryInfo.categoryId); + assertThat(actualCategoryInfo.categoryName).isEqualTo(expectedCategoryInfo.categoryName); + } +} diff --git a/src/test/java/com/nhnacademy/bookstorecoupon/config/QuerydslTestConfig.java b/src/test/java/com/nhnacademy/bookstorecoupon/config/QuerydslTestConfig.java new file mode 100644 index 0000000..2da9c34 --- /dev/null +++ b/src/test/java/com/nhnacademy/bookstorecoupon/config/QuerydslTestConfig.java @@ -0,0 +1,21 @@ +package com.nhnacademy.bookstorecoupon.config; + +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; + +import com.querydsl.jpa.impl.JPAQueryFactory; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; + +@TestConfiguration +public class QuerydslTestConfig { + + @PersistenceContext + private EntityManager entityManager; + + @Bean + public JPAQueryFactory jpaQueryFactory() { + return new JPAQueryFactory(this.entityManager); + } +} \ No newline at end of file diff --git a/src/test/java/com/nhnacademy/bookstorecoupon/couponpolicy/controller/CouponPolicyControllerTest.java b/src/test/java/com/nhnacademy/bookstorecoupon/couponpolicy/controller/CouponPolicyControllerTest.java index 45184cf..bab83bb 100644 --- a/src/test/java/com/nhnacademy/bookstorecoupon/couponpolicy/controller/CouponPolicyControllerTest.java +++ b/src/test/java/com/nhnacademy/bookstorecoupon/couponpolicy/controller/CouponPolicyControllerTest.java @@ -2,7 +2,6 @@ import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*; import static org.springframework.test.util.AssertionErrors.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @@ -12,13 +11,13 @@ 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.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.http.MediaType; -import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.web.servlet.MockMvc; import com.fasterxml.jackson.databind.ObjectMapper; @@ -33,6 +32,7 @@ * couponPolicyController 단위테스트 */ @WebMvcTest(CouponPolicyController.class) +@AutoConfigureMockMvc(addFilters = false) class CouponPolicyControllerTest { @Autowired @@ -41,15 +41,10 @@ class CouponPolicyControllerTest { @MockBean private CouponPolicyService couponPolicyService; - - @Autowired private ObjectMapper objectMapper; - - @Test - @WithMockUser(roles = {"COUPON_ADMIN"}) void testIssueWelcomeCoupon_Success() throws Exception { CouponPolicyRequestDTO requestDTO = new CouponPolicyRequestDTO( BigDecimal.valueOf(1000), // minOrderPrice @@ -65,7 +60,7 @@ void testIssueWelcomeCoupon_Success() throws Exception { mockMvc.perform(post("/coupons/policies/welcome") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(requestDTO)).with(csrf())) + .content(objectMapper.writeValueAsString(requestDTO))) .andExpect(status().isCreated()); @@ -73,7 +68,6 @@ void testIssueWelcomeCoupon_Success() throws Exception { } @Test - @WithMockUser(roles = {"COUPON_ADMIN"}) void testIssueWelcomeCoupon_InvalidRequest() throws Exception { CouponPolicyRequestDTO requestDTO = new CouponPolicyRequestDTO( null, // minOrderPrice @@ -89,14 +83,13 @@ void testIssueWelcomeCoupon_InvalidRequest() throws Exception { mockMvc.perform(post("/coupons/policies/welcome") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(requestDTO)).with(csrf())) + .content(objectMapper.writeValueAsString(requestDTO))) .andExpect(status().isBadRequest()); verify(couponPolicyService, times(0)).issueWelcomeCoupon(any(CouponPolicyRequestDTO.class)); } @Test - @WithMockUser(roles = {"COUPON_ADMIN"}) void testIssueBirthdayCoupon_Success() throws Exception { CouponPolicyRequestDTO requestDTO = new CouponPolicyRequestDTO( BigDecimal.valueOf(1000), // minOrderPrice @@ -112,14 +105,13 @@ void testIssueBirthdayCoupon_Success() throws Exception { mockMvc.perform(post("/coupons/policies/birthday") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(requestDTO)).with(csrf())) + .content(objectMapper.writeValueAsString(requestDTO))) .andExpect(status().isCreated()); verify(couponPolicyService, times(1)).issueBirthdayCoupon(any(CouponPolicyRequestDTO.class)); } @Test - @WithMockUser(roles = {"COUPON_ADMIN"}) void testIssueSpecificBookCoupon_Success() throws Exception { CouponPolicyRequestDTO requestDTO = new CouponPolicyRequestDTO( BigDecimal.valueOf(1000), // minOrderPrice @@ -135,14 +127,13 @@ void testIssueSpecificBookCoupon_Success() throws Exception { mockMvc.perform(post("/coupons/policies/books") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(requestDTO)).with(csrf())) + .content(objectMapper.writeValueAsString(requestDTO))) .andExpect(status().isCreated()); verify(couponPolicyService, times(1)).issueSpecificBookCoupon(any(CouponPolicyRequestDTO.class)); } @Test - @WithMockUser(roles = {"COUPON_ADMIN"}) void testIssueSpecificBookCoupon_BookIdOrTitleMissing() throws Exception { CouponPolicyRequestDTO requestDTO = new CouponPolicyRequestDTO( BigDecimal.valueOf(1000), // minOrderPrice @@ -158,13 +149,12 @@ void testIssueSpecificBookCoupon_BookIdOrTitleMissing() throws Exception { mockMvc.perform(post("/coupons/policies/books") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(requestDTO)).with(csrf())) + .content(objectMapper.writeValueAsString(requestDTO))) .andExpect(status().isBadRequest()); verify(couponPolicyService, times(0)).issueSpecificBookCoupon(any(CouponPolicyRequestDTO.class)); } @Test - @WithMockUser(roles = {"COUPON_ADMIN"}) void testIssueSpecificCategoryCoupon_Success() throws Exception { CouponPolicyRequestDTO requestDTO = new CouponPolicyRequestDTO( BigDecimal.valueOf(1000), // minOrderPrice @@ -180,14 +170,13 @@ void testIssueSpecificCategoryCoupon_Success() throws Exception { mockMvc.perform(post("/coupons/policies/categories") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(requestDTO)).with(csrf())) + .content(objectMapper.writeValueAsString(requestDTO))) .andExpect(status().isCreated()); verify(couponPolicyService, times(1)).issueSpecificCategoryCoupon(any(CouponPolicyRequestDTO.class)); } @Test - @WithMockUser(roles = {"COUPON_ADMIN"}) void testIssueSpecificCategoryCoupon_CategoryIdOrNameMissing() throws Exception { CouponPolicyRequestDTO requestDTO = new CouponPolicyRequestDTO( BigDecimal.valueOf(1000), // minOrderPrice @@ -203,7 +192,7 @@ void testIssueSpecificCategoryCoupon_CategoryIdOrNameMissing() throws Exception mockMvc.perform(post("/coupons/policies/categories") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(requestDTO)).with(csrf())) + .content(objectMapper.writeValueAsString(requestDTO))) .andExpect(status().isBadRequest()); @@ -211,7 +200,6 @@ void testIssueSpecificCategoryCoupon_CategoryIdOrNameMissing() throws Exception } @Test - @WithMockUser(roles = {"COUPON_ADMIN"}) void testIssueDiscountCoupon_Success() throws Exception { CouponPolicyRequestDTO requestDTO = new CouponPolicyRequestDTO( BigDecimal.valueOf(1000), // minOrderPrice @@ -227,14 +215,13 @@ void testIssueDiscountCoupon_Success() throws Exception { mockMvc.perform(post("/coupons/policies/sale") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(requestDTO)).with(csrf())) + .content(objectMapper.writeValueAsString(requestDTO))) .andExpect(status().isCreated()); verify(couponPolicyService, times(1)).issueDiscountCoupon(any(CouponPolicyRequestDTO.class)); } @Test - @WithMockUser(roles = {"COUPON_ADMIN"}) void testGetAllCouponPolicies_Success() throws Exception { CouponPolicyResponseDTO responseDTO = new CouponPolicyResponseDTO( 1L, @@ -254,7 +241,7 @@ void testGetAllCouponPolicies_Success() throws Exception { when(couponPolicyService.getAllCouponPolicies(any(Pageable.class))).thenReturn(page); mockMvc.perform(get("/coupons/policies") - .contentType(MediaType.APPLICATION_JSON).with(csrf())) + .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(jsonPath("$.content[0]").isNotEmpty()); @@ -262,7 +249,6 @@ void testGetAllCouponPolicies_Success() throws Exception { } @Test - @WithMockUser(roles = {"COUPON_ADMIN"}) void testUpdateCouponPolicy_Success() throws Exception { CouponPolicyUpdateRequestDTO requestDTO = new CouponPolicyUpdateRequestDTO( BigDecimal.valueOf(3000), @@ -274,14 +260,13 @@ void testUpdateCouponPolicy_Success() throws Exception { mockMvc.perform(patch("/coupons/policies/1") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(requestDTO)).with(csrf())) + .content(objectMapper.writeValueAsString(requestDTO))) .andExpect(status().isOk()); verify(couponPolicyService, times(1)).updateCouponPolicy(anyLong(), any(CouponPolicyUpdateRequestDTO.class)); } @Test - @WithMockUser(roles = {"COUPON_ADMIN"}) void testUpdateCouponPolicy_InvalidRequest() throws Exception { CouponPolicyUpdateRequestDTO requestDTO = new CouponPolicyUpdateRequestDTO( null, @@ -294,14 +279,13 @@ void testUpdateCouponPolicy_InvalidRequest() throws Exception { mockMvc.perform(patch("/coupons/policies/1") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(requestDTO)).with(csrf())) + .content(objectMapper.writeValueAsString(requestDTO))) .andExpect(status().isBadRequest()); verify(couponPolicyService, times(0)).updateCouponPolicy(anyLong(), any(CouponPolicyUpdateRequestDTO.class)); } @Test - @WithMockUser(roles = {"COUPON_ADMIN"}) void testIssueCoupon_SalePriceAndSaleRateInvalid() throws Exception { CouponPolicyRequestDTO requestDTO = new CouponPolicyRequestDTO( BigDecimal.valueOf(1000), // minOrderPrice @@ -317,8 +301,7 @@ void testIssueCoupon_SalePriceAndSaleRateInvalid() throws Exception { mockMvc.perform(post("/coupons/policies/welcome") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(requestDTO)) - .with(csrf())) + .content(objectMapper.writeValueAsString(requestDTO))) .andExpect(status().isBadRequest()) .andExpect(result -> { Throwable exception = result.getResolvedException(); @@ -346,7 +329,6 @@ void testIssueCoupon_SalePriceAndSaleRateInvalid() throws Exception { } @Test - @WithMockUser(roles = {"COUPON_ADMIN"}) void testIssueCoupon_SalePriceAndMaxSalePriceInvalid() throws Exception { CouponPolicyRequestDTO requestDTO = new CouponPolicyRequestDTO( BigDecimal.valueOf(1000), // minOrderPrice @@ -362,8 +344,7 @@ void testIssueCoupon_SalePriceAndMaxSalePriceInvalid() throws Exception { mockMvc.perform(post("/coupons/policies/welcome") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(requestDTO)) - .with(csrf())) + .content(objectMapper.writeValueAsString(requestDTO))) .andExpect(status().isBadRequest()) .andExpect(result -> { Throwable exception = result.getResolvedException(); @@ -392,7 +373,6 @@ void testIssueCoupon_SalePriceAndMaxSalePriceInvalid() throws Exception { @Test - @WithMockUser(roles = {"COUPON_ADMIN"}) void testUpdateCoupon_SalePriceAndMaxSalePriceSet() throws Exception { CouponPolicyUpdateRequestDTO requestDTO = new CouponPolicyUpdateRequestDTO( BigDecimal.valueOf(1000), // minOrderPrice @@ -404,8 +384,7 @@ void testUpdateCoupon_SalePriceAndMaxSalePriceSet() throws Exception { mockMvc.perform(patch("/coupons/policies/1") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(requestDTO)) - .with(csrf())) + .content(objectMapper.writeValueAsString(requestDTO))) .andExpect(status().isBadRequest()) .andExpect(result -> { Throwable exception = result.getResolvedException(); diff --git a/src/test/java/com/nhnacademy/bookstorecoupon/couponpolicy/domain/entity/CouponPolicyTest.java b/src/test/java/com/nhnacademy/bookstorecoupon/couponpolicy/domain/entity/CouponPolicyTest.java index d18bef0..0b371f2 100644 --- a/src/test/java/com/nhnacademy/bookstorecoupon/couponpolicy/domain/entity/CouponPolicyTest.java +++ b/src/test/java/com/nhnacademy/bookstorecoupon/couponpolicy/domain/entity/CouponPolicyTest.java @@ -9,6 +9,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; import com.nhnacademy.bookstorecoupon.couponpolicy.domain.dto.request.CouponPolicyRequestDTO; import com.nhnacademy.bookstorecoupon.couponpolicy.repository.CouponPolicyRepository; @@ -16,6 +17,7 @@ @DataJpaTest @Import(QuerydslConfig.class) +@ActiveProfiles("test") class CouponPolicyTest { @Autowired diff --git a/src/test/java/com/nhnacademy/bookstorecoupon/couponpolicy/service/impl/CouponPolicyServiceImplTest.java b/src/test/java/com/nhnacademy/bookstorecoupon/couponpolicy/service/impl/CouponPolicyServiceImplTest.java new file mode 100644 index 0000000..ba75211 --- /dev/null +++ b/src/test/java/com/nhnacademy/bookstorecoupon/couponpolicy/service/impl/CouponPolicyServiceImplTest.java @@ -0,0 +1,237 @@ +package com.nhnacademy.bookstorecoupon.couponpolicy.service.impl; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; + +import com.nhnacademy.bookstorecoupon.bookcoupon.domain.entity.BookCoupon; +import com.nhnacademy.bookstorecoupon.bookcoupon.repository.BookCouponRepository; +import com.nhnacademy.bookstorecoupon.categorycoupon.domain.entity.CategoryCoupon; +import com.nhnacademy.bookstorecoupon.categorycoupon.repository.CategoryCouponRepository; +import com.nhnacademy.bookstorecoupon.couponpolicy.domain.dto.request.CouponPolicyRequestDTO; +import com.nhnacademy.bookstorecoupon.couponpolicy.domain.dto.request.CouponPolicyUpdateRequestDTO; +import com.nhnacademy.bookstorecoupon.couponpolicy.domain.dto.response.CouponPolicyResponseDTO; +import com.nhnacademy.bookstorecoupon.couponpolicy.domain.entity.CouponPolicy; +import com.nhnacademy.bookstorecoupon.couponpolicy.exception.CouponPolicyNotFoundException; +import com.nhnacademy.bookstorecoupon.couponpolicy.repository.CouponPolicyRepository; +import com.nhnacademy.bookstorecoupon.userandcoupon.domain.entity.UserAndCoupon; +import com.nhnacademy.bookstorecoupon.userandcoupon.repository.UserAndCouponRepository; + + + class CouponPolicyServiceImplTest { + + @InjectMocks + private CouponPolicyServiceImpl couponPolicyService; + + @Mock + private CouponPolicyRepository couponPolicyRepository; + + @Mock + private BookCouponRepository bookCouponRepository; + + @Mock + private CategoryCouponRepository categoryCouponRepository; + + @Mock + private UserAndCouponRepository userAndCouponRepository; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + void testIssueWelcomeCoupon() { + CouponPolicyRequestDTO requestDTO = new CouponPolicyRequestDTO( + BigDecimal.valueOf(1000), BigDecimal.valueOf(200), null, null, "welcome", null, null, null, null + ); + + couponPolicyService.issueWelcomeCoupon(requestDTO); + + verify(couponPolicyRepository, times(1)).save(any(CouponPolicy.class)); + } + + @Test + void testIssueBirthdayCoupon() { + CouponPolicyRequestDTO requestDTO = new CouponPolicyRequestDTO( + BigDecimal.valueOf(1000), BigDecimal.valueOf(200), null, + null, "birthday", null, null, null, null + ); + + couponPolicyService.issueBirthdayCoupon(requestDTO); + + verify(couponPolicyRepository, times(1)).save(any(CouponPolicy.class)); + } + + @Test + void testIssueSpecificBookCoupon() { + CouponPolicyRequestDTO requestDTO = new CouponPolicyRequestDTO( + BigDecimal.valueOf(1000), BigDecimal.valueOf(200), null, + null, "book", 1L, "Book Title", null, null + ); + + couponPolicyService.issueSpecificBookCoupon(requestDTO); + + verify(couponPolicyRepository, times(1)).save(any(CouponPolicy.class)); + verify(bookCouponRepository, times(1)).save(any(BookCoupon.class)); + } + + @Test + void testIssueSpecificCategoryCoupon() { + CouponPolicyRequestDTO requestDTO = new CouponPolicyRequestDTO( + BigDecimal.valueOf(1000), BigDecimal.valueOf(200), null, + null, "category", null, null, 1L, "Category Name" + ); + + couponPolicyService.issueSpecificCategoryCoupon(requestDTO); + + verify(couponPolicyRepository, times(1)).save(any(CouponPolicy.class)); + verify(categoryCouponRepository, times(1)).save(any(CategoryCoupon.class)); + } + + @Test + void testIssueDiscountCoupon() { + CouponPolicyRequestDTO requestDTO = new CouponPolicyRequestDTO( + BigDecimal.valueOf(1000), BigDecimal.valueOf(200), null, + null, "sale", null, null, null, null + ); + + couponPolicyService.issueDiscountCoupon(requestDTO); + + verify(couponPolicyRepository, times(1)).save(any(CouponPolicy.class)); + } + + @Test + void testGetAllCouponPolicies() { + Pageable pageable = PageRequest.of(0, 10); + Map bookIdMap = Map.of(); + Map categoryIdMap = Map.of(); + + CouponPolicyResponseDTO couponPolicyResponseDTO = CouponPolicyResponseDTO.builder() + .categoryName(null).categoryId(null).bookTitle("title") + .id(1L).type("book").bookId(1L).minOrderPrice(BigDecimal.valueOf(100)) + .maxSalePrice(null).salePrice(BigDecimal.valueOf(200)).saleRate(null) + .isUsed(true).build(); + Page couponPolicyPage = new PageImpl<>(List.of(couponPolicyResponseDTO)); + + when(couponPolicyRepository.findAllWithBooksAndCategories(pageable, bookIdMap, categoryIdMap)) + .thenReturn(couponPolicyPage); + + couponPolicyService.getAllCouponPolicies(pageable); + + verify(couponPolicyRepository, times(1)).findAllWithBooksAndCategories(any(Pageable.class), anyMap(), anyMap()); + } + @Test + void testUpdateCouponPolicyWhenPolicyExistsAndIsNotUsed() { + Long policyId = 1L; + CouponPolicyUpdateRequestDTO requestDTO = new CouponPolicyUpdateRequestDTO( + BigDecimal.valueOf(2000), BigDecimal.valueOf(400), null, + null, false // isUsed = false + ); + + CouponPolicy existingPolicy = CouponPolicy.builder() + .minOrderPrice(BigDecimal.valueOf(100)) + .salePrice(BigDecimal.valueOf(100)) + .type("sale") + .maxSalePrice(null) + .saleRate(null) + .build(); + + UserAndCoupon userAndCoupon = mock(UserAndCoupon.class); + List userAndCoupons = List.of(userAndCoupon); + + // Create a spy for existingPolicy + CouponPolicy spiedPolicy = spy(existingPolicy); + + // Mock repository methods + when(couponPolicyRepository.findById(policyId)).thenReturn(Optional.of(spiedPolicy)); + when(userAndCouponRepository.findByCouponPolicy(spiedPolicy)).thenReturn(userAndCoupons); + + // Call the method under test + couponPolicyService.updateCouponPolicy(policyId, requestDTO); + + // Verify interactions + verify(couponPolicyRepository, times(1)).findById(policyId); + verify(userAndCouponRepository, times(1)).findByCouponPolicy(spiedPolicy); + verify(spiedPolicy, times(1)).update( + BigDecimal.valueOf(2000), + BigDecimal.valueOf(400), + null, + null, + false + ); // Verify the update method was called with the correct arguments + verify(userAndCoupon, times(1)).update(any(), eq(true)); // Verify if update is called with true for isUsed + } + + @Test + void testUpdateCouponPolicyWhenPolicyExistsAndIsUsed() { + Long policyId = 1L; + CouponPolicyUpdateRequestDTO requestDTO = new CouponPolicyUpdateRequestDTO( + BigDecimal.valueOf(2000), BigDecimal.valueOf(400), null, + null, true // isUsed = true + ); + + CouponPolicy existingPolicy = CouponPolicy.builder() + .minOrderPrice(BigDecimal.valueOf(100)) + .salePrice(BigDecimal.valueOf(100)) + .type("sale") + .maxSalePrice(null) + .saleRate(null) + .build(); + + // Create a spy for existingPolicy + CouponPolicy spiedPolicy = spy(existingPolicy); + + + + // Mock repository methods + when(couponPolicyRepository.findById(policyId)).thenReturn(Optional.of(spiedPolicy)); + + // Call the method under test + couponPolicyService.updateCouponPolicy(policyId, requestDTO); + + // Verify interactions + verify(couponPolicyRepository, times(1)).findById(policyId); + verify(userAndCouponRepository, never()).findByCouponPolicy(any()); // Ensure findByCouponPolicy is not called + verify(spiedPolicy, times(1)).update( + BigDecimal.valueOf(2000), + BigDecimal.valueOf(400), + null, + null, + true + ); // Verify the update method was called with the correct arguments + } + + @Test + void testUpdateCouponPolicyNotFound() { + Long policyId = 1L; + CouponPolicyUpdateRequestDTO requestDTO = new CouponPolicyUpdateRequestDTO( + BigDecimal.valueOf(2000), BigDecimal.valueOf(400), null, + null, false + ); + + when(couponPolicyRepository.findById(policyId)).thenReturn(Optional.empty()); + + CouponPolicyNotFoundException exception = assertThrows(CouponPolicyNotFoundException.class, () -> + couponPolicyService.updateCouponPolicy(policyId, requestDTO) + ); + + assertEquals("해당 쿠폰정책번호 '1'는 존재하지 않습니다.", exception.getErrorStatus().getMessage()); + assertEquals(HttpStatus.NOT_FOUND, exception.getErrorStatus().getStatus()); + } + } \ No newline at end of file diff --git a/src/test/java/com/nhnacademy/bookstorecoupon/coupontemplate/controller/CouponTemplateControllerTest.java b/src/test/java/com/nhnacademy/bookstorecoupon/coupontemplate/controller/CouponTemplateControllerTest.java index 041f1cc..fbb218f 100644 --- a/src/test/java/com/nhnacademy/bookstorecoupon/coupontemplate/controller/CouponTemplateControllerTest.java +++ b/src/test/java/com/nhnacademy/bookstorecoupon/coupontemplate/controller/CouponTemplateControllerTest.java @@ -2,7 +2,6 @@ import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @@ -13,13 +12,13 @@ import org.junit.jupiter.api.Test; 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.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.http.MediaType; -import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.web.servlet.MockMvc; import com.fasterxml.jackson.databind.ObjectMapper; @@ -28,6 +27,7 @@ import com.nhnacademy.bookstorecoupon.coupontemplate.service.CouponTemplateService; @WebMvcTest(CouponTemplateController.class) +@AutoConfigureMockMvc(addFilters = false) class CouponTemplateControllerTest { @Autowired @@ -39,8 +39,9 @@ class CouponTemplateControllerTest { @MockBean private CouponTemplateService couponTemplateService; + + @Test - @WithMockUser(roles = {"COUPON_ADMIN"}) void testCreateCouponTemplate_Valid() throws Exception { CouponTemplateRequestDTO validRequest = new CouponTemplateRequestDTO( 1L, @@ -51,14 +52,13 @@ void testCreateCouponTemplate_Valid() throws Exception { mockMvc.perform(post("/coupons") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(validRequest)).with(csrf())) + .content(objectMapper.writeValueAsString(validRequest))) .andExpect(status().isCreated()); verify(couponTemplateService, times(1)).createCouponTemplate(any(CouponTemplateRequestDTO.class)); } @Test - @WithMockUser(roles = {"COUPON_ADMIN"}) void testCreateCouponTemplate_InvalidCouponPolicyId() throws Exception { CouponTemplateRequestDTO invalidRequest = new CouponTemplateRequestDTO( null, @@ -69,7 +69,7 @@ void testCreateCouponTemplate_InvalidCouponPolicyId() throws Exception { mockMvc.perform(post("/coupons") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(invalidRequest)).with(csrf())) + .content(objectMapper.writeValueAsString(invalidRequest))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("$.message").value("couponPolicyId 에러: must not be null, 입력된 값: null; ")) .andExpect(jsonPath("$.status").value("BAD_REQUEST")); @@ -78,7 +78,6 @@ void testCreateCouponTemplate_InvalidCouponPolicyId() throws Exception { } @Test - @WithMockUser(roles = {"COUPON_ADMIN"}) void testCreateCouponTemplate_InvalidExpiredDate() throws Exception { CouponTemplateRequestDTO invalidRequest = new CouponTemplateRequestDTO( 1L, @@ -89,7 +88,7 @@ void testCreateCouponTemplate_InvalidExpiredDate() throws Exception { mockMvc.perform(post("/coupons") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(invalidRequest)).with(csrf())) + .content(objectMapper.writeValueAsString(invalidRequest))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("$.message").value("expiredDate 에러: must not be null, 입력된 값: null; ")) .andExpect(jsonPath("$.status").value("BAD_REQUEST")); @@ -98,7 +97,6 @@ void testCreateCouponTemplate_InvalidExpiredDate() throws Exception { } @Test - @WithMockUser(roles = {"COUPON_ADMIN"}) void testCreateCouponTemplate_InvalidIssueDate() throws Exception { CouponTemplateRequestDTO invalidRequest = new CouponTemplateRequestDTO( 1L, @@ -109,7 +107,7 @@ void testCreateCouponTemplate_InvalidIssueDate() throws Exception { mockMvc.perform(post("/coupons") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(invalidRequest)).with(csrf())) + .content(objectMapper.writeValueAsString(invalidRequest))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("$.message").value("issueDate 에러: must not be null, 입력된 값: null; ")) .andExpect(jsonPath("$.status").value("BAD_REQUEST")); @@ -118,7 +116,6 @@ void testCreateCouponTemplate_InvalidIssueDate() throws Exception { } @Test - @WithMockUser(roles = {"COUPON_ADMIN"}) void testCreateCouponTemplate_InvalidQuantity() throws Exception { CouponTemplateRequestDTO invalidRequest = new CouponTemplateRequestDTO( 1L, @@ -129,7 +126,7 @@ void testCreateCouponTemplate_InvalidQuantity() throws Exception { mockMvc.perform(post("/coupons") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(invalidRequest)).with(csrf())) + .content(objectMapper.writeValueAsString(invalidRequest))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("$.message").value("quantity 에러: must not be null, 입력된 값: null; ")) .andExpect(jsonPath("$.status").value("BAD_REQUEST")); @@ -138,7 +135,6 @@ void testCreateCouponTemplate_InvalidQuantity() throws Exception { } @Test - @WithMockUser(roles = {"COUPON_ADMIN"}) void testCreateCouponTemplate_NegativeQuantity() throws Exception { CouponTemplateRequestDTO invalidRequest = new CouponTemplateRequestDTO( 1L, @@ -149,7 +145,7 @@ void testCreateCouponTemplate_NegativeQuantity() throws Exception { mockMvc.perform(post("/coupons") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(invalidRequest)).with(csrf())) + .content(objectMapper.writeValueAsString(invalidRequest))) .andExpect(status().isBadRequest()) .andExpect(jsonPath("$.message").value("quantity 에러: must be greater than 0, 입력된 값: -100; ")) .andExpect(jsonPath("$.status").value("BAD_REQUEST")); @@ -158,7 +154,6 @@ void testCreateCouponTemplate_NegativeQuantity() throws Exception { } @Test - @WithMockUser(roles = {"MEMBER"}) void testGetAllCouponTemplatesByUserPaging() throws Exception { // CouponTemplateResponseDTO 객체 생성 CouponTemplateResponseDTO coupon1 = new CouponTemplateResponseDTO( @@ -190,8 +185,7 @@ void testGetAllCouponTemplatesByUserPaging() throws Exception { mockMvc.perform(get("/coupons/issue") .param("page", "0") // 페이지는 0부터 시작하므로 0으로 설정 .param("size", "3") - .contentType(MediaType.APPLICATION_JSON) - .with(csrf())) + .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.content[0].id").value(1)) @@ -208,7 +202,6 @@ void testGetAllCouponTemplatesByUserPaging() throws Exception { } @Test - @WithMockUser(roles = {"COUPON_ADMIN"}) // 올바른 역할로 설정 void testGetAllCouponTemplatesByManagerPaging() throws Exception { // CouponTemplateResponseDTO 객체 생성 CouponTemplateResponseDTO coupon1 = new CouponTemplateResponseDTO( @@ -240,8 +233,7 @@ void testGetAllCouponTemplatesByManagerPaging() throws Exception { mockMvc.perform(get("/coupons") .param("page", "0") // 페이지는 0부터 시작하므로 0으로 설정 .param("size", "2") - .contentType(MediaType.APPLICATION_JSON) - .with(csrf())) // CSRF 토큰을 포함 + .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.content[0].id").value(1)) diff --git a/src/test/java/com/nhnacademy/bookstorecoupon/coupontemplate/domain/entity/CouponTemplateTest.java b/src/test/java/com/nhnacademy/bookstorecoupon/coupontemplate/domain/entity/CouponTemplateTest.java index d6b5c58..20fabc9 100644 --- a/src/test/java/com/nhnacademy/bookstorecoupon/coupontemplate/domain/entity/CouponTemplateTest.java +++ b/src/test/java/com/nhnacademy/bookstorecoupon/coupontemplate/domain/entity/CouponTemplateTest.java @@ -10,6 +10,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; import com.nhnacademy.bookstorecoupon.couponpolicy.domain.entity.CouponPolicy; import com.nhnacademy.bookstorecoupon.couponpolicy.repository.CouponPolicyRepository; @@ -18,6 +19,7 @@ @DataJpaTest @Import(QuerydslConfig.class) +@ActiveProfiles("test") class CouponTemplateTest { @Autowired diff --git a/src/test/java/com/nhnacademy/bookstorecoupon/coupontemplate/repository/impl/CustomCouponTemplateRepositoryImplIntegrationTest.java b/src/test/java/com/nhnacademy/bookstorecoupon/coupontemplate/repository/impl/CustomCouponTemplateRepositoryImplIntegrationTest.java new file mode 100644 index 0000000..3a6b1d8 --- /dev/null +++ b/src/test/java/com/nhnacademy/bookstorecoupon/coupontemplate/repository/impl/CustomCouponTemplateRepositoryImplIntegrationTest.java @@ -0,0 +1,157 @@ +package com.nhnacademy.bookstorecoupon.coupontemplate.repository.impl; + +import static org.junit.jupiter.api.Assertions.*; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.AfterEach; +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.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +import com.nhnacademy.bookstorecoupon.bookcoupon.domain.entity.BookCoupon; +import com.nhnacademy.bookstorecoupon.categorycoupon.domain.entity.CategoryCoupon; +import com.nhnacademy.bookstorecoupon.config.QuerydslTestConfig; +import com.nhnacademy.bookstorecoupon.couponpolicy.domain.entity.CouponPolicy; +import com.nhnacademy.bookstorecoupon.coupontemplate.domain.dto.response.CouponTemplateResponseDTO; +import com.nhnacademy.bookstorecoupon.coupontemplate.domain.entity.CouponTemplate; +import com.querydsl.jpa.impl.JPAQueryFactory; + +import jakarta.persistence.EntityManager; + +@DataJpaTest +@Import(QuerydslTestConfig.class) +public class CustomCouponTemplateRepositoryImplIntegrationTest { + + @Autowired + private EntityManager entityManager; + + @Autowired + private JPAQueryFactory queryFactory; + + private CustomCouponTemplateRepositoryImpl customCouponTemplateRepository; + + @BeforeEach + void setUp() { + customCouponTemplateRepository = new CustomCouponTemplateRepositoryImpl(queryFactory); + + // Create CouponPolicy + CouponPolicy couponPolicy = CouponPolicy.builder() + .minOrderPrice(BigDecimal.valueOf(1000)) + .salePrice(BigDecimal.valueOf(100)) + .saleRate(null) + .maxSalePrice(null) + .type("book") + .build(); + + + CouponPolicy couponPolicy2 = CouponPolicy.builder() + .minOrderPrice(BigDecimal.valueOf(1000)) + .salePrice(BigDecimal.valueOf(100)) + .saleRate(null) + .maxSalePrice(null) + .type("sale") + .build(); + + + CouponPolicy couponPolicy3 = CouponPolicy.builder() + .minOrderPrice(BigDecimal.valueOf(1000)) + .salePrice(BigDecimal.valueOf(100)) + .saleRate(null) + .maxSalePrice(null) + .type("category") + .build(); + + entityManager.persist(couponPolicy); + entityManager.persist(couponPolicy2); + entityManager.persist(couponPolicy3); + + // Create BookCoupon + BookCoupon bookCoupon = BookCoupon.builder() + .couponPolicy(couponPolicy) + .bookId(123L) + .bookTitle("Sample Book") + .build(); + entityManager.persist(bookCoupon); + + // Create CategoryCoupon + CategoryCoupon categoryCoupon = CategoryCoupon.builder() + .couponPolicy(couponPolicy2) + .categoryId(456L) + .categoryName("Sample Category") + .build(); + entityManager.persist(categoryCoupon); + + // Create CouponTemplate + CouponTemplate couponTemplate = CouponTemplate.builder() + .couponPolicy(couponPolicy) + .expiredDate(LocalDateTime.now().plusMonths(1)) + .issueDate(LocalDateTime.now()) + .quantity(100L) + .build(); + + CouponTemplate couponTemplate2 = CouponTemplate.builder() + .couponPolicy(couponPolicy2) + .expiredDate(LocalDateTime.now().plusMonths(1)) + .issueDate(LocalDateTime.now()) + .quantity(100L) + .build(); + + CouponTemplate couponTemplate3 = CouponTemplate.builder() + .couponPolicy(couponPolicy3) + .expiredDate(LocalDateTime.now().plusMonths(1)) + .issueDate(LocalDateTime.now()) + .quantity(0L) + .build(); + entityManager.persist(couponTemplate); + entityManager.persist(couponTemplate2); + entityManager.persist(couponTemplate3); + + + } + + @AfterEach + void resetAutoIncrementId() { + entityManager.createNativeQuery("ALTER TABLE coupons_templates ALTER COLUMN coupon_template_id RESTART WITH 1") + .executeUpdate(); + } + + @Test + void testFindAllTemplatesByManagerPaging() { + // Given + Pageable pageable = Pageable.ofSize(10); + Map bookIdMap = new HashMap<>(); + Map categoryIdMap = new HashMap<>(); + + // When + Page result = customCouponTemplateRepository.findAllTemplatesByManagerPaging( + pageable, bookIdMap, categoryIdMap); + + // Then + assertEquals(3, result.getTotalElements()); + assertEquals(3, result.getContent().size()); + } + + @Test + void testFindAllTemplatesByUserPaging() { + // Given + Pageable pageable = Pageable.ofSize(10); + Map bookIdMap = new HashMap<>(); + Map categoryIdMap = new HashMap<>(); + + // When + Page result = customCouponTemplateRepository.findAllTemplatesByUserPaging(pageable, + bookIdMap, categoryIdMap); + + // Then + assertEquals(2, result.getTotalElements()); + assertEquals(2, result.getContent().size()); + } +} \ No newline at end of file diff --git a/src/test/java/com/nhnacademy/bookstorecoupon/coupontemplate/repository/impl/CustomCouponTemplateRepositoryImplTest.java b/src/test/java/com/nhnacademy/bookstorecoupon/coupontemplate/repository/impl/CustomCouponTemplateRepositoryImplUnitTest.java similarity index 99% rename from src/test/java/com/nhnacademy/bookstorecoupon/coupontemplate/repository/impl/CustomCouponTemplateRepositoryImplTest.java rename to src/test/java/com/nhnacademy/bookstorecoupon/coupontemplate/repository/impl/CustomCouponTemplateRepositoryImplUnitTest.java index 910e621..a2046b1 100644 --- a/src/test/java/com/nhnacademy/bookstorecoupon/coupontemplate/repository/impl/CustomCouponTemplateRepositoryImplTest.java +++ b/src/test/java/com/nhnacademy/bookstorecoupon/coupontemplate/repository/impl/CustomCouponTemplateRepositoryImplUnitTest.java @@ -27,7 +27,7 @@ import com.querydsl.jpa.impl.JPAQuery; import com.querydsl.jpa.impl.JPAQueryFactory; -class CustomCouponTemplateRepositoryImplTest { +class CustomCouponTemplateRepositoryImplUnitTest { @Mock private JPAQueryFactory queryFactory; @@ -165,5 +165,4 @@ void testFindAllTemplatesByManagerPaging() { verify(countQuery).fetchOne(); } - } \ No newline at end of file diff --git a/src/test/java/com/nhnacademy/bookstorecoupon/coupontemplate/service/impl/CouponTemplateServiceImplTest.java b/src/test/java/com/nhnacademy/bookstorecoupon/coupontemplate/service/impl/CouponTemplateServiceImplTest.java new file mode 100644 index 0000000..4a87972 --- /dev/null +++ b/src/test/java/com/nhnacademy/bookstorecoupon/coupontemplate/service/impl/CouponTemplateServiceImplTest.java @@ -0,0 +1,276 @@ +package com.nhnacademy.bookstorecoupon.coupontemplate.service.impl; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.lang.reflect.Field; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; + +import com.nhnacademy.bookstorecoupon.bookcoupon.domain.entity.BookCoupon; +import com.nhnacademy.bookstorecoupon.bookcoupon.repository.BookCouponRepository; +import com.nhnacademy.bookstorecoupon.categorycoupon.domain.entity.CategoryCoupon; +import com.nhnacademy.bookstorecoupon.categorycoupon.repository.CategoryCouponRepository; +import com.nhnacademy.bookstorecoupon.couponpolicy.domain.entity.CouponPolicy; +import com.nhnacademy.bookstorecoupon.couponpolicy.exception.CouponPolicyNotFoundException; +import com.nhnacademy.bookstorecoupon.couponpolicy.repository.CouponPolicyRepository; +import com.nhnacademy.bookstorecoupon.coupontemplate.domain.dto.request.CouponTemplateRequestDTO; +import com.nhnacademy.bookstorecoupon.coupontemplate.domain.dto.response.CouponTemplateResponseDTO; +import com.nhnacademy.bookstorecoupon.coupontemplate.domain.entity.CouponTemplate; +import com.nhnacademy.bookstorecoupon.coupontemplate.exception.CouponTemplateAddErrorException; +import com.nhnacademy.bookstorecoupon.coupontemplate.repository.CouponTemplateRepository; + +class CouponTemplateServiceImplTest { + + @Mock + private CouponTemplateRepository couponTemplateRepository; + + @Mock + private CouponPolicyRepository couponPolicyRepository; + + @Mock + private BookCouponRepository bookCouponRepository; + + @Mock + private CategoryCouponRepository categoryCouponRepository; + + @InjectMocks + private CouponTemplateServiceImpl couponTemplateService; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + + @Test + void testCreateCouponTemplateSuccess() { + Long couponPolicyId = 1L; + Long templateId = 1L; // 이 ID가 일관되게 사용되도록 합니다. + + // CouponPolicy 객체 설정 + CouponPolicy couponPolicy = CouponPolicy.builder() + .minOrderPrice(BigDecimal.valueOf(100)) + .salePrice(BigDecimal.valueOf(10)) + .saleRate(null) + .maxSalePrice(null) + .type("sale") + .build(); + + + + // 요청 DTO 설정 + CouponTemplateRequestDTO requestDTO = new CouponTemplateRequestDTO( + couponPolicyId, + LocalDateTime.now().plusDays(30), + LocalDateTime.now(), + 100L + ); + + // couponPolicyRepository의 모의 동작 설정 + when(couponPolicyRepository.findById(couponPolicyId)).thenReturn(Optional.of(couponPolicy)); + + // 테스트할 메소드 호출 + couponTemplateService.createCouponTemplate(requestDTO); + + // 예상되는 CouponTemplate 객체 생성 + CouponTemplate expectedTemplate = CouponTemplate.builder() + .couponPolicy(couponPolicy) + .expiredDate(requestDTO.expiredDate()) + .issueDate(requestDTO.issueDate()) + .quantity(requestDTO.quantity()) + .build(); + + // CouponTemplate을 저장한 후 모의 동작 설정 + when(couponTemplateRepository.findById(templateId)).thenReturn(Optional.of(expectedTemplate)); + + // save 메소드가 올바른 CouponTemplate으로 호출되었는지 검증 + CouponTemplate actualTemplate = couponTemplateRepository.findById(templateId).orElseThrow(); + + // 실제 객체와 예상 객체 비교 + assertEquals(expectedTemplate, actualTemplate); + } + + @Test + void testCreateCouponTemplateWhenPolicyNotFound() { + Long couponPolicyId = 1L; + CouponTemplateRequestDTO requestDTO = new CouponTemplateRequestDTO( + couponPolicyId, + LocalDateTime.now().plusDays(30), + LocalDateTime.now(), + 100L + ); + + when(couponPolicyRepository.findById(couponPolicyId)).thenReturn(Optional.empty()); + + CouponPolicyNotFoundException thrown = assertThrows(CouponPolicyNotFoundException.class, () -> + couponTemplateService.createCouponTemplate(requestDTO) + ); + + assertEquals("해당 쿠폰정책번호 '1'는 존재하지 않습니다.", thrown.getErrorStatus().getMessage()); + assertEquals(HttpStatus.NOT_FOUND, thrown.getErrorStatus().getStatus()); + } + + @Test + void testCreateCouponTemplateWhenPolicyTypeNotAllowed() { + Long couponPolicyId = 1L; + CouponPolicy couponPolicy = CouponPolicy.builder() + .minOrderPrice(BigDecimal.valueOf(100)) + .salePrice(BigDecimal.valueOf(10)) + .saleRate(null) + .maxSalePrice(null) + .type("birthday") // Ensure type is set to "birthday" for this test + .build(); + + CouponTemplateRequestDTO requestDTO = new CouponTemplateRequestDTO( + couponPolicyId, + LocalDateTime.now().plusDays(30), + LocalDateTime.now(), + 100L + ); + + when(couponPolicyRepository.findById(couponPolicyId)).thenReturn(Optional.of(couponPolicy)); + + CouponTemplateAddErrorException thrown = assertThrows(CouponTemplateAddErrorException.class, () -> + couponTemplateService.createCouponTemplate(requestDTO) + ); + + assertEquals("해당 쿠폰타입 'birthday'은 템플릿 발급을 할 수 없습니다.", thrown.getErrorStatus().getMessage()); + assertEquals(HttpStatus.BAD_REQUEST, thrown.getErrorStatus().getStatus()); + } + + @Test + void testCreateCouponTemplateWhenPolicyIsUsed() throws NoSuchFieldException, IllegalAccessException { + Long couponPolicyId = 1L; + CouponPolicy couponPolicy = CouponPolicy.builder() + .minOrderPrice(BigDecimal.valueOf(100)) + .salePrice(BigDecimal.valueOf(10)) + .saleRate(null) + .maxSalePrice(null) + .type("sale") + .build(); + + + // Use reflection to set `isUsed` to true + Field isUsedField = CouponPolicy.class.getDeclaredField("isUsed"); + isUsedField.setAccessible(true); + isUsedField.set(couponPolicy, false); + + CouponTemplateRequestDTO requestDTO = new CouponTemplateRequestDTO( + couponPolicyId, + LocalDateTime.now().plusDays(30), + LocalDateTime.now(), + 100L + ); + + when(couponPolicyRepository.findById(couponPolicyId)).thenReturn(Optional.of(couponPolicy)); + + CouponTemplateAddErrorException thrown = assertThrows(CouponTemplateAddErrorException.class, () -> + couponTemplateService.createCouponTemplate(requestDTO) + ); + + assertEquals("해당 쿠폰템플릿은 정책폐기로 인해 발급을 할 수 없습니다.", thrown.getErrorStatus().getMessage()); + assertEquals(HttpStatus.FORBIDDEN, thrown.getErrorStatus().getStatus()); + } + + @Test + void testGetAllCouponTemplatesByManagerPaging() { + Pageable pageable = PageRequest.of(0, 10); + CouponTemplateResponseDTO responseDTO = CouponTemplateResponseDTO.builder() + .id(1L) + .couponPolicyId(1L) + .minOrderPrice(BigDecimal.valueOf(100)) + .salePrice(BigDecimal.valueOf(10)) + .saleRate(null) + .maxSalePrice(null) + .type("book") + .isUsed(true) + .bookId(1L) + .bookTitle("Sample Book") + .categoryId(null) + .categoryName(null) + .expiredDate(LocalDateTime.now().plusDays(30)) + .issueDate(LocalDateTime.now()) + .quantity(100L) + .build(); + + Page page = new PageImpl<>(List.of(responseDTO), pageable, 1); + + // Create a Map with specific types + Map bookInfoMap = Map.of(1L, new BookCoupon.BookInfo(1L, "Sample Book")); + Map categoryInfoMap = Map.of(1L, new CategoryCoupon.CategoryInfo(1L, "Sample Category")); + + // Ensure that mock methods are called + when(bookCouponRepository.fetchBookIdMap()).thenReturn(bookInfoMap); + when(categoryCouponRepository.fetchCategoryIdMap()).thenReturn(categoryInfoMap); + + // Match the exact arguments in the when clause + when(couponTemplateRepository.findAllTemplatesByManagerPaging(eq(pageable), eq(bookInfoMap), eq(categoryInfoMap))).thenReturn(page); + + // Execute the method under test + Page result = couponTemplateService.getAllCouponTemplatesByManagerPaging(pageable); + + // Verify the result + assertNotNull(result, "Result should not be null"); + assertEquals(1, result.getTotalElements(), "Total elements should match"); + assertEquals(responseDTO, result.getContent().getFirst(), "Response DTO should match"); + + } + @Test + void testGetAllCouponTemplatesByUserPaging() { + Pageable pageable = PageRequest.of(0, 10); + CouponTemplateResponseDTO responseDTO = CouponTemplateResponseDTO.builder() + .id(1L) + .couponPolicyId(1L) + .minOrderPrice(BigDecimal.valueOf(100)) + .salePrice(BigDecimal.valueOf(10)) + .saleRate(null) + .maxSalePrice(null) + .type("book") + .isUsed(true) + .bookId(1L) + .bookTitle("Sample Book") + .categoryId(null) + .categoryName(null) + .expiredDate(LocalDateTime.now().plusDays(30)) + .issueDate(LocalDateTime.now()) + .quantity(100L) + .build(); + + Page page = new PageImpl<>(List.of(responseDTO), pageable, 1); + + // Create a Map with specific types + Map bookInfoMap = Map.of(1L, new BookCoupon.BookInfo(1L, "Sample Book")); + Map categoryInfoMap = Map.of(1L, new CategoryCoupon.CategoryInfo(1L, "Sample Category")); + + // Ensure that mock methods are called + when(bookCouponRepository.fetchBookIdMap()).thenReturn(bookInfoMap); + when(categoryCouponRepository.fetchCategoryIdMap()).thenReturn(categoryInfoMap); + + // Match the exact arguments in the when clause + when(couponTemplateRepository.findAllTemplatesByUserPaging(eq(pageable), eq(bookInfoMap), eq(categoryInfoMap))).thenReturn(page); + + // Execute the method under test + Page result = couponTemplateService.getAllCouponTemplatesByUserPaging(pageable); + + // Verify the result + assertNotNull(result, "Result should not be null"); + assertEquals(1, result.getTotalElements(), "Total elements should match"); + assertEquals(responseDTO, result.getContent().getFirst(), "Response DTO should match"); + + } +} diff --git a/src/test/java/com/nhnacademy/bookstorecoupon/global/aspect/RoleAuthorizationAspectTest.java b/src/test/java/com/nhnacademy/bookstorecoupon/global/aspect/RoleAuthorizationAspectTest.java new file mode 100644 index 0000000..bd3169c --- /dev/null +++ b/src/test/java/com/nhnacademy/bookstorecoupon/global/aspect/RoleAuthorizationAspectTest.java @@ -0,0 +1,111 @@ +package com.nhnacademy.bookstorecoupon.global.aspect; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.Collection; +import java.util.HashSet; + +import org.aspectj.lang.JoinPoint; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import com.nhnacademy.bookstorecoupon.auth.annotation.AuthorizeRole; +import com.nhnacademy.bookstorecoupon.auth.jwt.dto.CurrentUserDetails; +import com.nhnacademy.bookstorecoupon.global.exception.InavailableAuthorizationException; + +class RoleAuthorizationAspectTest { + + @Mock + private JoinPoint joinPoint; + + @Mock + private SecurityContext securityContext; + + @Mock + private Authentication authentication; + + @Mock + private CurrentUserDetails currentUserDetails; + + @InjectMocks + private RoleAuthorizationAspect roleAuthorizationAspect; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + SecurityContextHolder.setContext(securityContext); + } + + + @Test + void testCheckUserRole_MissingRole_ThrowsException() { + // Arrange + String[] roles = {"ROLE_ADMIN"}; + AuthorizeRole authorizeRole = mock(AuthorizeRole.class); + when(authorizeRole.value()).thenReturn(roles); + + Collection authorities = new HashSet<>(); + authorities.add(new SimpleGrantedAuthority("ROLE_USER")); + when(authentication.getPrincipal()).thenReturn(currentUserDetails); + when(securityContext.getAuthentication()).thenReturn(authentication); + RequestContextHolder.setRequestAttributes(mock(ServletRequestAttributes.class)); + + // Act & Assert + InavailableAuthorizationException thrown = assertThrows( + InavailableAuthorizationException.class, + () -> roleAuthorizationAspect.checkUserRole(joinPoint, authorizeRole) + ); + + assertNotNull(thrown); + } + + @Test + void testCheckUserRole_NoAuthentication_ThrowsException() { + // Arrange + String[] roles = {"ROLE_USER"}; + AuthorizeRole authorizeRole = mock(AuthorizeRole.class); + when(authorizeRole.value()).thenReturn(roles); + + when(securityContext.getAuthentication()).thenReturn(null); + RequestContextHolder.setRequestAttributes(mock(ServletRequestAttributes.class)); + + // Act & Assert + InavailableAuthorizationException thrown = assertThrows( + InavailableAuthorizationException.class, + () -> roleAuthorizationAspect.checkUserRole(joinPoint, authorizeRole) + ); + + assertNotNull(thrown); + } + + @Test + void testCheckUserRole_NoCurrentUserDetails_ThrowsException() { + // Arrange + String[] roles = {"ROLE_USER"}; + AuthorizeRole authorizeRole = mock(AuthorizeRole.class); + when(authorizeRole.value()).thenReturn(roles); + + when(authentication.getPrincipal()).thenReturn(null); + when(securityContext.getAuthentication()).thenReturn(authentication); + RequestContextHolder.setRequestAttributes(mock(ServletRequestAttributes.class)); + + // Act & Assert + InavailableAuthorizationException thrown = assertThrows( + InavailableAuthorizationException.class, + () -> roleAuthorizationAspect.checkUserRole(joinPoint, authorizeRole) + ); + + assertNotNull(thrown); + } +} diff --git a/src/test/java/com/nhnacademy/bookstorecoupon/global/controller/SwaggerControllerTest.java b/src/test/java/com/nhnacademy/bookstorecoupon/global/controller/SwaggerControllerTest.java new file mode 100644 index 0000000..153bf8f --- /dev/null +++ b/src/test/java/com/nhnacademy/bookstorecoupon/global/controller/SwaggerControllerTest.java @@ -0,0 +1,76 @@ +package com.nhnacademy.bookstorecoupon.global.controller; + +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.nio.charset.StandardCharsets; +import java.util.Locale; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockitoAnnotations; +import org.springdoc.webmvc.api.OpenApiResource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +import jakarta.servlet.http.HttpServletRequest; +@WebMvcTest(SwaggerController.class) +@AutoConfigureMockMvc(addFilters = false) +class SwaggerControllerTest { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private OpenApiResource openApiResource; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + void testGetSwaggerJson() throws Exception { + // Mock Swagger JSON response + String mockSwaggerJson = "{ \"openapi\": \"3.0.0\" }"; + byte[] mockSwaggerJsonBytes = mockSwaggerJson.getBytes(StandardCharsets.UTF_8); + + // Mock OpenApiResource to return byte[] array + when(openApiResource.openapiJson(any(HttpServletRequest.class), eq("/coupon-docs/coupon-api"), any(Locale.class))) + .thenReturn(mockSwaggerJsonBytes); + + // Perform request and validate response + mockMvc.perform(get("/coupons/api") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(content().json(mockSwaggerJson)); + } + + @Test + void testGetSwaggerJson_whenEmpty() throws Exception { + // Mock empty Swagger JSON response + String emptySwaggerJson = "{}"; + byte[] emptySwaggerJsonBytes = emptySwaggerJson.getBytes(StandardCharsets.UTF_8); + + // Mock HttpServletRequest + HttpServletRequest mockRequest = mock(HttpServletRequest.class); + when(mockRequest.getLocale()).thenReturn(Locale.getDefault()); + + // Configure mock behavior with HttpServletRequest and Locale + when(openApiResource.openapiJson(eq(mockRequest), eq("/coupon-docs/coupon-api"), any(Locale.class))) + .thenReturn(emptySwaggerJsonBytes); + + // Perform request and validate response + mockMvc.perform(get("/coupons/api") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(content().json(emptySwaggerJson)); + } +} \ No newline at end of file diff --git a/src/test/java/com/nhnacademy/bookstorecoupon/global/listener/CouponIssuanceListenerTest.java b/src/test/java/com/nhnacademy/bookstorecoupon/global/listener/CouponIssuanceListenerTest.java new file mode 100644 index 0000000..531e1f1 --- /dev/null +++ b/src/test/java/com/nhnacademy/bookstorecoupon/global/listener/CouponIssuanceListenerTest.java @@ -0,0 +1,120 @@ +// package com.nhnacademy.bookstorecoupon.global.listener; +// +// import static org.junit.jupiter.api.Assertions.*; +// import static org.mockito.Mockito.*; +// +// import java.math.BigDecimal; +// import java.time.LocalDateTime; +// import java.util.Optional; +// +// import org.junit.jupiter.api.BeforeEach; +// import org.junit.jupiter.api.Test; +// import org.mockito.InjectMocks; +// import org.mockito.Mock; +// import org.mockito.MockitoAnnotations; +// import org.springframework.amqp.core.Message; +// import org.springframework.amqp.core.MessageProperties; +// import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; +// +// import com.fasterxml.jackson.databind.DeserializationFeature; +// import com.fasterxml.jackson.databind.ObjectMapper; +// import com.nhnacademy.bookstorecoupon.couponpolicy.domain.entity.CouponPolicy; +// import com.nhnacademy.bookstorecoupon.coupontemplate.domain.entity.CouponTemplate; +// import com.nhnacademy.bookstorecoupon.coupontemplate.repository.CouponTemplateRepository; +// import com.nhnacademy.bookstorecoupon.userandcoupon.domain.dto.request.CouponIssuanceMessage; +// import com.nhnacademy.bookstorecoupon.userandcoupon.domain.entity.UserAndCoupon; +// import com.nhnacademy.bookstorecoupon.userandcoupon.repository.UserAndCouponRepository; +// +// class CouponIssuanceListenerTest { +// +// @Mock +// private UserAndCouponRepository userAndCouponRepository; +// +// @Mock +// private CouponTemplateRepository couponTemplateRepository; +// +// @InjectMocks +// private CouponIssuanceListener couponIssuanceListener; +// +// private final ObjectMapper objectMapper = new ObjectMapper() +// .findAndRegisterModules() +// .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); +// +// private final Jackson2JsonMessageConverter jsonMessageConverter = new Jackson2JsonMessageConverter(objectMapper); +// +// @BeforeEach +// void setUp() { +// MockitoAnnotations.openMocks(this); +// } +// +// private Message createMockMessage(CouponIssuanceMessage message) { +// try { +// byte[] messageBody = objectMapper.writeValueAsBytes(message); +// MessageProperties messageProperties = new MessageProperties(); +// return new Message(messageBody, messageProperties); +// } catch (Exception e) { +// throw new RuntimeException("Failed to create mock message", e); +// } +// } +// +// +// +// +// @Test +// void testOnMessage_CouponTemplateRepositoryThrowsException() { +// // Arrange +// Long couponId = 1L; +// Long userId = 1L; +// CouponIssuanceMessage message = new CouponIssuanceMessage(couponId, userId); +// +// Message mockMessage = createMockMessage(message); +// +// // Mock behavior for repository +// when(couponTemplateRepository.findById(couponId)).thenThrow(new RuntimeException("Database error")); +// +// // Act & Assert +// RuntimeException thrown = assertThrows( +// RuntimeException.class, +// () -> couponIssuanceListener.onMessage(mockMessage) +// ); +// +// assertTrue(thrown.getMessage().contains("Failed to process message")); +// } +// +// @Test +// void testOnMessage_UserAndCouponRepositoryThrowsException() { +// // Arrange +// Long couponId = 1L; +// Long userId = 1L; +// CouponIssuanceMessage message = new CouponIssuanceMessage(couponId, userId); +// +// CouponPolicy couponPolicy = CouponPolicy.builder() +// .minOrderPrice(BigDecimal.valueOf(1000)) +// .salePrice(BigDecimal.valueOf(100)) +// .saleRate(null) +// .maxSalePrice(null) +// .type("sale") +// .build(); +// +// CouponTemplate couponTemplate = CouponTemplate.builder() +// .couponPolicy(couponPolicy) +// .expiredDate(LocalDateTime.now().plusDays(30)) +// .issueDate(LocalDateTime.now()) +// .quantity(10L) +// .build(); +// +// Message mockMessage = createMockMessage(message); +// +// // Mock behavior for repositories +// when(couponTemplateRepository.findById(couponId)).thenReturn(Optional.of(couponTemplate)); +// doThrow(new RuntimeException("Database error")).when(userAndCouponRepository).save(any(UserAndCoupon.class)); +// +// // Act & Assert +// RuntimeException thrown = assertThrows( +// RuntimeException.class, +// () -> couponIssuanceListener.onMessage(mockMessage) +// ); +// +// assertTrue(thrown.getMessage().contains("Failed to process message")); +// } +// } \ No newline at end of file diff --git a/src/test/java/com/nhnacademy/bookstorecoupon/user/domain/entity/UserRoleTest.java b/src/test/java/com/nhnacademy/bookstorecoupon/user/domain/entity/UserRoleTest.java new file mode 100644 index 0000000..8e6241c --- /dev/null +++ b/src/test/java/com/nhnacademy/bookstorecoupon/user/domain/entity/UserRoleTest.java @@ -0,0 +1,46 @@ +package com.nhnacademy.bookstorecoupon.user.domain.entity; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.Test; + +class UserRoleTest { + + @Test + void testBuilder() { + // Arrange + User user = mock(User.class); + Role role = mock(Role.class); + + // Act + UserRole userRole = UserRole.builder() + .user(user) + .role(role) + .build(); + + // Assert + assertEquals(user, userRole.getUser()); + assertEquals(role, userRole.getRole()); + } + + @Test + void testGetRoleName() { + // Arrange + Role role = mock(Role.class); + String roleName = "ADMIN"; + when(role.getRoleName()).thenReturn(roleName); + User user = mock(User.class); + + UserRole userRole = UserRole.builder() + .user(user) + .role(role) + .build(); + + // Act + String actualRoleName = userRole.getRoleName(); + + // Assert + assertEquals(roleName, actualRoleName); + } +} diff --git a/src/test/java/com/nhnacademy/bookstorecoupon/user/domain/entity/UserStatusTest.java b/src/test/java/com/nhnacademy/bookstorecoupon/user/domain/entity/UserStatusTest.java new file mode 100644 index 0000000..27833a8 --- /dev/null +++ b/src/test/java/com/nhnacademy/bookstorecoupon/user/domain/entity/UserStatusTest.java @@ -0,0 +1,37 @@ +package com.nhnacademy.bookstorecoupon.user.domain.entity; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +import com.nhnacademy.bookstorecoupon.user.domain.dto.request.CreateUserStatusRequest; + +class UserStatusTest { + + @Test + void testBuilder() { + // Arrange + String userStatusName = "ACTIVE"; + + // Act + UserStatus userStatus = UserStatus.builder() + .userStatusName(userStatusName) + .build(); + + // Assert + assertEquals(userStatusName, userStatus.getUserStatusName()); + } + + @Test + void testToEntity() { + // Arrange + String userStatusName = "INACTIVE"; + CreateUserStatusRequest request = new CreateUserStatusRequest(userStatusName); + + // Act + UserStatus userStatus = UserStatus.toEntity(request); + + // Assert + assertEquals(userStatusName, userStatus.getUserStatusName()); + } +} diff --git a/src/test/java/com/nhnacademy/bookstorecoupon/user/domain/entity/UserTest.java b/src/test/java/com/nhnacademy/bookstorecoupon/user/domain/entity/UserTest.java new file mode 100644 index 0000000..4280099 --- /dev/null +++ b/src/test/java/com/nhnacademy/bookstorecoupon/user/domain/entity/UserTest.java @@ -0,0 +1,42 @@ +package com.nhnacademy.bookstorecoupon.user.domain.entity; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class UserTest { + + private User user; + private UserRole userRoleAdmin; + private UserRole userRoleUser; + + @BeforeEach + void setUp() { + user = new User(); + userRoleAdmin = mock(UserRole.class); + userRoleUser = mock(UserRole.class); + } + + @Test + void testGetAllRoles() { + // Arrange + String adminRoleName = "ADMIN"; + String userRoleName = "USER"; + when(userRoleAdmin.getRoleName()).thenReturn(adminRoleName); + when(userRoleUser.getRoleName()).thenReturn(userRoleName); + + user.getUserRoles().addAll(Arrays.asList(userRoleAdmin, userRoleUser)); + + // Act + List roles = user.getAllRoles(); + + // Assert + assertEquals(Arrays.asList(adminRoleName, userRoleName), roles); + } + +} \ No newline at end of file diff --git a/src/test/java/com/nhnacademy/bookstorecoupon/userandcoupon/controller/UserAndCouponControllerTest.java b/src/test/java/com/nhnacademy/bookstorecoupon/userandcoupon/controller/UserAndCouponControllerTest.java index 90bad8f..b8a8fb8 100644 --- a/src/test/java/com/nhnacademy/bookstorecoupon/userandcoupon/controller/UserAndCouponControllerTest.java +++ b/src/test/java/com/nhnacademy/bookstorecoupon/userandcoupon/controller/UserAndCouponControllerTest.java @@ -14,6 +14,7 @@ import org.junit.jupiter.api.Test; 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.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.data.domain.Page; @@ -21,9 +22,13 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.http.MediaType; -import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; import com.fasterxml.jackson.databind.ObjectMapper; import com.nhnacademy.bookstorecoupon.auth.jwt.dto.CurrentUserDetails; @@ -35,6 +40,7 @@ import com.nhnacademy.bookstorecoupon.userandcoupon.service.impl.RabbitMQUserAndCouponService; @WebMvcTest(UserAndCouponController.class) +@AutoConfigureMockMvc(addFilters = false) class UserAndCouponControllerTest { @Autowired @@ -52,7 +58,8 @@ class UserAndCouponControllerTest { @Autowired private ObjectMapper objectMapper; - + @Autowired + private WebApplicationContext context; private UserAndCouponResponseDTO dto1; private UserAndCouponResponseDTO dto2; @@ -67,25 +74,31 @@ class UserAndCouponControllerTest { @BeforeEach void setUp() { // UserTokenInfo 및 CurrentUserDetails 초기화 - UserTokenInfo userTokenInfo = UserTokenInfo.builder() + + + + currentMember = new CurrentUserDetails(UserTokenInfo.builder() .id(1L) .password("password") .roles(List.of("ROLE_MEMBER")) .status("ACTIVE") - .build(); + .build()); + setSecurityContext(currentMember); - currentMember = new CurrentUserDetails(userTokenInfo); - UserTokenInfo userTokenInfo2 = UserTokenInfo.builder() + currentAdmin = new CurrentUserDetails(UserTokenInfo.builder() .id(1L) .password("password") .roles(List.of("ROLE_COUPON_ADMIN")) .status("ACTIVE") - .build(); + .build()); + setSecurityContext(currentAdmin); + + + - currentAdmin = new CurrentUserDetails(userTokenInfo2); // 공통적으로 사용할 DTO 객체 초기화 @@ -127,14 +140,25 @@ void setUp() { bookDetail2 = new GetBookByOrderCouponResponse( 4L, BigDecimal.valueOf(30000), List.of(1L) ); + mockMvc = MockMvcBuilders + .webAppContextSetup(context) + .build(); + } + private void setSecurityContext(CurrentUserDetails currentUserDetails) { + SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); + securityContext.setAuthentication(new UsernamePasswordAuthenticationToken(currentUserDetails, "password", currentUserDetails.getAuthorities())); + SecurityContextHolder.setContext(securityContext); } @Test - @WithMockUser(roles = "MEMBER") void testCreateUserAndCoupon() throws Exception { Long couponId = 1L; + + + + // Arrange - Set up the mock behavior // You can use `Mockito` to mock behaviors here if needed doNothing().when(rabbitMQUserAndCouponService).createUserAndCoupon(couponId, 1L); @@ -143,7 +167,6 @@ void testCreateUserAndCoupon() throws Exception { mockMvc.perform(post("/coupons/{couponId}", couponId) .contentType(MediaType.APPLICATION_JSON) .header("Authorization", "Bearer dummyToken") - .with(csrf()) .with(user(currentMember))) .andExpect(status().isCreated()); @@ -152,15 +175,13 @@ void testCreateUserAndCoupon() throws Exception { } @Test - @WithMockUser(roles = "COUPON_ADMIN") void testCreateUserWelcomeCouponIssue() throws Exception { Long userId = 1L; mockMvc.perform(post("/coupons/coupon/welcome") .param("userId", "1") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(userId)) - .with(csrf())) + .content(objectMapper.writeValueAsString(userId))) .andExpect(status().isCreated()); verify(userAndCouponService, times(1)).createUserWelcomeCouponIssue(userId); @@ -168,12 +189,10 @@ void testCreateUserWelcomeCouponIssue() throws Exception { @Test - @WithMockUser(roles = "COUPON_ADMIN") void testFailCreateUserWelcomeCouponIssue() throws Exception { // Arrange - Setting up the test without userId mockMvc.perform(post("/coupons/coupon/welcome") - .contentType(MediaType.APPLICATION_JSON) - .with(csrf())) + .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isBadRequest()) // Expecting 400 Bad Request .andExpect(jsonPath("$.message").value("유저 아이디가 필요합니다.")) // Check the error message .andExpect(jsonPath("$.status").value("BAD_REQUEST")); // Check the error status @@ -184,7 +203,6 @@ void testFailCreateUserWelcomeCouponIssue() throws Exception { @Test - @WithMockUser(roles = "MEMBER") void testGetAllUserAndCouponsByUserPaging() throws Exception { @@ -203,7 +221,6 @@ void testGetAllUserAndCouponsByUserPaging() throws Exception { .param("page", "1") .param("size", "2") .header("Authorization", "Bearer dummyToken") - .with(csrf()) .with(user(currentMember))) .andExpect(status().isOk()) .andExpect(jsonPath("$.content[0].id").value(1)) @@ -215,7 +232,6 @@ void testGetAllUserAndCouponsByUserPaging() throws Exception { } @Test - @WithMockUser(roles = "COUPON_ADMIN") void testGetAllUsersAndCouponsByManagerPaging() throws Exception { Page dtoPage = new PageImpl<>(List.of(dto1, dto2)); @@ -232,7 +248,6 @@ void testGetAllUsersAndCouponsByManagerPaging() throws Exception { .param("page", "1") .param("size", "2") .header("Authorization", "Bearer dummyToken") - .with(csrf()) .with(user(currentAdmin))) .andExpect(status().isOk()) .andExpect(jsonPath("$.content[0].id").value(1)) @@ -244,7 +259,6 @@ void testGetAllUsersAndCouponsByManagerPaging() throws Exception { } @Test - @WithMockUser(roles = "MEMBER") void testFindCouponByOrder() throws Exception { // 준비: 공통 DTO 객체를 사용 List userAndCouponResponseList = List.of(dto3, dto4); @@ -263,7 +277,6 @@ void testFindCouponByOrder() throws Exception { .param("bookIds", "1") // Query 파라미터로 전달 .param("categoryIds", "1") // Query 파라미터로 전달 .header("Authorization", "Bearer dummyToken") - .with(csrf()) .with(user(currentMember))) .andExpect(status().isOk()) .andExpect(jsonPath("$[0].id").value(3)) @@ -277,7 +290,6 @@ void testFindCouponByOrder() throws Exception { } @Test - @WithMockUser(roles = "MEMBER") void testFindCouponByCartOrder() throws Exception { // 여러 개의 GetBookByOrderCouponResponse 객체를 포함하는 리스트 생성 List bookDetails = Arrays.asList(bookDetail1, bookDetail2); @@ -290,7 +302,6 @@ void testFindCouponByCartOrder() throws Exception { .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(bookDetails)) .header("Authorization", "Bearer dummyToken") - .with(csrf()) .with(user(currentMember))) .andExpect(status().isOk()) .andExpect(jsonPath("$[0].id").value(dto3.id())) @@ -304,7 +315,6 @@ void testFindCouponByCartOrder() throws Exception { @Test - @WithMockUser(roles = "MEMBER") void testUpdateCouponAfterPayment() throws Exception { // Arrange Long userAndCouponId = 1L; @@ -315,7 +325,6 @@ void testUpdateCouponAfterPayment() throws Exception { // Act & Assert mockMvc.perform(patch("/coupons/users/payment/{userAndCouponId}", userAndCouponId) .header("Authorization", "Bearer dummyToken") - .with(csrf()) .with(user(currentMember))) .andExpect(status().isOk()); @@ -325,7 +334,6 @@ void testUpdateCouponAfterPayment() throws Exception { @Test - @WithMockUser(roles = "MEMBER") void testGetSelectedCouponWithValidCouponId() throws Exception { Long validCouponId = 1L; @@ -334,8 +342,7 @@ void testGetSelectedCouponWithValidCouponId() throws Exception { mockMvc.perform(get("/coupons/users/order/coupon") .param("couponId", validCouponId.toString()) - .header("Authorization", "Bearer dummyToken") - .with(csrf())) + .header("Authorization", "Bearer dummyToken")) .andExpect(status().isOk()) .andExpect(jsonPath("$.id").value(1)) .andExpect(jsonPath("$.type").value("sale")); @@ -349,9 +356,12 @@ void testGetSelectedCouponWithValidCouponId() throws Exception { @Test void testIsRealUserCheckWhenCurrentUserDetailsIsNull() throws Exception { + SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); + securityContext.setAuthentication(new UsernamePasswordAuthenticationToken(null, "password")); + SecurityContextHolder.setContext(securityContext); + mockMvc.perform(MockMvcRequestBuilders.get("/coupons/users/auth") .header("Authorization", "Bearer dummyToken") - .with(csrf()) .with(user("1").roles("MEMBER")) // This is just to have a user, but you need to simulate the null user .with(request -> { // Simulate a null currentUserDetails @@ -364,11 +374,9 @@ void testIsRealUserCheckWhenCurrentUserDetailsIsNull() throws Exception { @Test - @WithMockUser(roles = "MEMBER") void testIsRealUserCheckWhenCurrentUserDetailsIsNotNull() throws Exception { mockMvc.perform(get("/coupons/users/auth") .header("Authorization", "Bearer dummyToken") - .with(csrf()) .with(user(currentMember))) .andExpect(status().isOk()) .andExpect(jsonPath("$").value(true)); diff --git a/src/test/java/com/nhnacademy/bookstorecoupon/userandcoupon/domain/entity/UserAndCouponTest.java b/src/test/java/com/nhnacademy/bookstorecoupon/userandcoupon/domain/entity/UserAndCouponTest.java index fa2e73a..68caf0e 100644 --- a/src/test/java/com/nhnacademy/bookstorecoupon/userandcoupon/domain/entity/UserAndCouponTest.java +++ b/src/test/java/com/nhnacademy/bookstorecoupon/userandcoupon/domain/entity/UserAndCouponTest.java @@ -10,6 +10,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; import com.nhnacademy.bookstorecoupon.couponpolicy.domain.entity.CouponPolicy; import com.nhnacademy.bookstorecoupon.couponpolicy.repository.CouponPolicyRepository; @@ -18,6 +19,7 @@ @DataJpaTest @Import(QuerydslConfig.class) +@ActiveProfiles("test") public class UserAndCouponTest { @Autowired diff --git a/src/test/java/com/nhnacademy/bookstorecoupon/userandcoupon/repository/impl/CustomUserAndCouponRepositoryImplTest.java b/src/test/java/com/nhnacademy/bookstorecoupon/userandcoupon/repository/impl/CustomUserAndCouponRepositoryImplTest.java new file mode 100644 index 0000000..b80e821 --- /dev/null +++ b/src/test/java/com/nhnacademy/bookstorecoupon/userandcoupon/repository/impl/CustomUserAndCouponRepositoryImplTest.java @@ -0,0 +1,356 @@ +package com.nhnacademy.bookstorecoupon.userandcoupon.repository.impl; + +import static org.junit.jupiter.api.Assertions.*; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.Collections; +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.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.test.annotation.DirtiesContext; + +import com.nhnacademy.bookstorecoupon.bookcoupon.domain.entity.BookCoupon; +import com.nhnacademy.bookstorecoupon.categorycoupon.domain.entity.CategoryCoupon; +import com.nhnacademy.bookstorecoupon.config.QuerydslTestConfig; +import com.nhnacademy.bookstorecoupon.couponpolicy.domain.entity.CouponPolicy; +import com.nhnacademy.bookstorecoupon.userandcoupon.domain.dto.response.GetBookByOrderCouponResponse; +import com.nhnacademy.bookstorecoupon.userandcoupon.domain.dto.response.UserAndCouponResponseDTO; +import com.nhnacademy.bookstorecoupon.userandcoupon.domain.entity.UserAndCoupon; + +import jakarta.persistence.EntityManager; + +@DataJpaTest +@Import(QuerydslTestConfig.class) +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) +class CustomUserAndCouponRepositoryImplTest { + + @Autowired + private EntityManager entityManager; + + private CustomUserAndCouponRepositoryImpl repository; + + + + @BeforeEach + void setUp() { + repository = new CustomUserAndCouponRepositoryImpl(entityManager); + } + + + @Test + void testFindAllByUserPaging() { + // 데이터 설정 + CouponPolicy couponPolicy = CouponPolicy.builder() + .minOrderPrice(BigDecimal.valueOf(100)) + .salePrice(BigDecimal.valueOf(10)) + .saleRate(null) + .maxSalePrice(null) + .type("book") + .build(); + + + + couponPolicy.update(BigDecimal.valueOf(100), BigDecimal.valueOf(10), null, null, true); + entityManager.persist(couponPolicy); + + UserAndCoupon userAndCoupon = UserAndCoupon.builder() + .couponPolicy(couponPolicy) + .userId(1L) + .expiredDate(LocalDateTime.now().plusDays(1)) + .issueDate(LocalDateTime.now()) + .isUsed(false) + .usedDate(null) + .build(); + + UserAndCoupon userAndCoupon2 = UserAndCoupon.builder() + .couponPolicy(couponPolicy) + .userId(1L) + .expiredDate(LocalDateTime.now().plusDays(1)) + .issueDate(LocalDateTime.now()) + .isUsed(false) + .usedDate(null) + .build(); + entityManager.persist(userAndCoupon); + entityManager.persist(userAndCoupon2); + + Map bookIdMap = new HashMap<>(); + bookIdMap.put(1L, new BookCoupon.BookInfo(1L, "Book Title")); + + Map categoryIdMap = new HashMap<>(); + Pageable pageable = PageRequest.of(0, 10); + + + + + + // 테스트 실행 + Page result = repository.findAllByUserPaging(pageable, 1L, bookIdMap, categoryIdMap); + + // 검증 + assertNotNull(result); + assertEquals(2, result.getTotalElements()); + UserAndCouponResponseDTO dto = result.getContent().get(0); + assertEquals(2L, dto.id()); + assertEquals(1L, dto.userId()); + assertNotNull(dto.expiredDate()); + assertNotNull(dto.issueDate()); + assertEquals(0, BigDecimal.valueOf(100).compareTo(dto.minOrderPrice())); + assertEquals(0, BigDecimal.valueOf(10).compareTo(dto.salePrice())); + assertEquals("book", dto.type()); + assertTrue(dto.policyIsUsed()); + assertEquals(1L, dto.bookId()); + assertEquals("Book Title", dto.bookTitle()); + + + } + + @Test + void testFindAllByManagerPaging() { + // 데이터 설정 + CouponPolicy couponPolicy = CouponPolicy.builder() + .minOrderPrice(BigDecimal.valueOf(100)) + .salePrice(BigDecimal.valueOf(10)) + .saleRate(null) + .maxSalePrice(null) + .type("welcome") + .build(); + + + + + + couponPolicy.update(BigDecimal.valueOf(100), BigDecimal.valueOf(10), null, null, true); + entityManager.persist(couponPolicy); + + UserAndCoupon userAndCoupon = UserAndCoupon.builder() + .couponPolicy(couponPolicy) + .userId(1L) + .expiredDate(LocalDateTime.now().plusDays(1)) + .issueDate(LocalDateTime.now()) + .isUsed(false) + .usedDate(null) + .build(); + + UserAndCoupon userAndCoupon2 = UserAndCoupon.builder() + .couponPolicy(couponPolicy) + .userId(1L) + .expiredDate(LocalDateTime.now().plusDays(1)) + .issueDate(LocalDateTime.now()) + .isUsed(false) + .usedDate(null) + .build(); + + entityManager.persist(userAndCoupon); + entityManager.persist(userAndCoupon2); + + Map bookIdMap = new HashMap<>(); + Map categoryIdMap = new HashMap<>(); + Pageable pageable = PageRequest.of(0, 10); + + // 테스트 실행 + Page result = repository.findAllByManagerPaging(pageable, "welcome", 1L, bookIdMap, categoryIdMap); + + // 검증 + assertNotNull(result); + assertEquals(2, result.getTotalElements()); + UserAndCouponResponseDTO dto = result.getContent().get(0); + assertEquals(2L, dto.id()); + assertEquals(1L, dto.userId()); + assertNotNull(dto.expiredDate()); + assertNotNull(dto.issueDate()); + assertEquals(0, BigDecimal.valueOf(100).compareTo(dto.minOrderPrice())); + assertEquals(0, BigDecimal.valueOf(10).compareTo(dto.salePrice())); + assertEquals("welcome", dto.type()); + assertTrue(dto.policyIsUsed()); + assertNull(dto.bookId()); + assertNull(dto.bookTitle()); + assertNull(dto.categoryId()); + assertNull(dto.categoryName()); + } + + @Test + void testFindCouponByOrder() { + // 데이터 설정 + CouponPolicy couponPolicy = CouponPolicy.builder() + .minOrderPrice(BigDecimal.valueOf(100)) + .salePrice(BigDecimal.valueOf(10)) + .saleRate(null) + .maxSalePrice(null) + .type("book") + .build(); + + + CouponPolicy couponPolicy2 = CouponPolicy.builder() + .minOrderPrice(BigDecimal.valueOf(100)) + .salePrice(BigDecimal.valueOf(10)) + .saleRate(null) + .maxSalePrice(null) + .type("welcome") + .build(); + + couponPolicy.update(BigDecimal.valueOf(100), BigDecimal.valueOf(10), null, null, true); + couponPolicy.update(BigDecimal.valueOf(100), BigDecimal.valueOf(10), null, null, true); + couponPolicy.update(BigDecimal.valueOf(100), BigDecimal.valueOf(10), null, null, true); + entityManager.persist(couponPolicy); + entityManager.persist(couponPolicy2); + + UserAndCoupon userAndCoupon = UserAndCoupon.builder() + .couponPolicy(couponPolicy) + .userId(1L) + .expiredDate(LocalDateTime.now().plusDays(1)) + .issueDate(LocalDateTime.now()) + .isUsed(false) + .usedDate(null) + .expiredDate(LocalDateTime.now().plusDays(365)) + .issueDate(LocalDateTime.now().minusDays(10)) + .build(); + + + + UserAndCoupon userAndCoupon2 = UserAndCoupon.builder() + .couponPolicy(couponPolicy2) + .userId(1L) + .expiredDate(LocalDateTime.now().plusDays(1)) + .issueDate(LocalDateTime.now()) + .isUsed(false) + .usedDate(null) + .expiredDate(LocalDateTime.now().plusDays(365)) + .issueDate(LocalDateTime.now().minusDays(10)) + .build(); + entityManager.persist(userAndCoupon); + entityManager.persist(userAndCoupon2); + + Map bookIdMap = new HashMap<>(); + bookIdMap.put(1L, new BookCoupon.BookInfo(1L, "Book Title")); + + + Map categoryIdMap = new HashMap<>(); + + + List bookIds = Collections.singletonList(1L); + List categoryIds = Collections.emptyList(); + BigDecimal bookPrice = BigDecimal.valueOf(100); + + // 테스트 실행 + List results = repository.findCouponByOrder( + 1L, + bookIdMap, + categoryIdMap, + bookIds, + categoryIds, + bookPrice + ); + + // 검증 + assertNotNull(results); + assertEquals(2, results.size()); + UserAndCouponResponseDTO dto = results.get(0); + assertEquals(2L, dto.id()); + assertEquals(1L, dto.userId()); + assertNotNull(dto.expiredDate()); + assertNotNull(dto.issueDate()); + assertEquals(0, BigDecimal.valueOf(100).compareTo(dto.minOrderPrice())); + assertEquals(0, BigDecimal.valueOf(10).compareTo(dto.salePrice())); + assertEquals("welcome", dto.type()); + assertTrue(dto.policyIsUsed()); + + } + + @Test + void testFindCouponByCartOrder() { + // 데이터 설정 + CouponPolicy couponPolicy = CouponPolicy.builder() + .minOrderPrice(BigDecimal.valueOf(100)) + .salePrice(BigDecimal.valueOf(10)) + .saleRate(null) + .maxSalePrice(null) + .type("book") + .build(); + + + CouponPolicy couponPolicy2 = CouponPolicy.builder() + .minOrderPrice(BigDecimal.valueOf(100)) + .salePrice(BigDecimal.valueOf(10)) + .saleRate(null) + .maxSalePrice(null) + .type("welcome") + .build(); + + couponPolicy.update(BigDecimal.valueOf(100), BigDecimal.valueOf(10), null, null, true); + couponPolicy.update(BigDecimal.valueOf(100), BigDecimal.valueOf(10), null, null, true); + couponPolicy.update(BigDecimal.valueOf(100), BigDecimal.valueOf(10), null, null, true); + entityManager.persist(couponPolicy); + entityManager.persist(couponPolicy2); + + UserAndCoupon userAndCoupon = UserAndCoupon.builder() + .couponPolicy(couponPolicy) + .userId(1L) + .expiredDate(LocalDateTime.now().plusDays(1)) + .issueDate(LocalDateTime.now()) + .isUsed(false) + .usedDate(null) + .expiredDate(LocalDateTime.now().plusDays(365)) + .issueDate(LocalDateTime.now().minusDays(10)) + .build(); + + + + UserAndCoupon userAndCoupon2 = UserAndCoupon.builder() + .couponPolicy(couponPolicy2) + .userId(1L) + .expiredDate(LocalDateTime.now().plusDays(1)) + .issueDate(LocalDateTime.now()) + .isUsed(false) + .usedDate(null) + .expiredDate(LocalDateTime.now().plusDays(365)) + .issueDate(LocalDateTime.now().minusDays(10)) + .build(); + entityManager.persist(userAndCoupon); + entityManager.persist(userAndCoupon2); + + Map bookIdMap = new HashMap<>(); + bookIdMap.put(1L, new BookCoupon.BookInfo(1L, "Book Title")); + + + Map categoryIdMap = new HashMap<>(); + + + // GetBookByOrderCouponResponse 리스트 생성 + List bookResponses = Collections.singletonList( + new GetBookByOrderCouponResponse(1L, BigDecimal.valueOf(100), Collections.emptyList()) + ); + + // 테스트 실행 + List results = repository.findCouponByCartOrder( + 1L, + bookIdMap, + categoryIdMap, + bookResponses + ); + + // 검증 + assertNotNull(results); + assertEquals(2, results.size()); + UserAndCouponResponseDTO dto = results.get(0); + assertEquals(2L, dto.id()); + assertEquals(1L, dto.userId()); + assertNotNull(dto.expiredDate()); + assertNotNull(dto.issueDate()); + assertEquals(0, BigDecimal.valueOf(100).compareTo(dto.minOrderPrice())); + assertEquals(0, BigDecimal.valueOf(10).compareTo(dto.salePrice())); + assertEquals("welcome", dto.type()); + assertTrue(dto.policyIsUsed()); + } +} \ No newline at end of file diff --git a/src/test/java/com/nhnacademy/bookstorecoupon/userandcoupon/service/impl/RabbitMQUserAndCouponServiceTest.java b/src/test/java/com/nhnacademy/bookstorecoupon/userandcoupon/service/impl/RabbitMQUserAndCouponServiceTest.java new file mode 100644 index 0000000..00efc41 --- /dev/null +++ b/src/test/java/com/nhnacademy/bookstorecoupon/userandcoupon/service/impl/RabbitMQUserAndCouponServiceTest.java @@ -0,0 +1,102 @@ +package com.nhnacademy.bookstorecoupon.userandcoupon.service.impl; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.Optional; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.http.HttpStatus; + +import com.nhnacademy.bookstorecoupon.global.exception.payload.ErrorStatus; +import com.nhnacademy.bookstorecoupon.userandcoupon.domain.dto.request.CouponIssuanceMessage; +import com.nhnacademy.bookstorecoupon.userandcoupon.exception.UserCouponValidationException; + +class RabbitMQUserAndCouponServiceTest { + + @Mock + private RabbitTemplate rabbitTemplate; + + @InjectMocks + private RabbitMQUserAndCouponService rabbitMQUserAndCouponService; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + void testCreateUserAndCoupon_ValidInputs() { + // Arrange + Long couponId = 1L; + Long userId = 1L; + CouponIssuanceMessage expectedMessage = new CouponIssuanceMessage(couponId, userId); + + // Act + rabbitMQUserAndCouponService.createUserAndCoupon(couponId, userId); + + // Assert + verify(rabbitTemplate, times(1)).convertAndSend( + eq("5ritang.coupon.exchange"), + eq("5ritang.coupon.key"), + Optional.ofNullable(argThat(message -> + message instanceof CouponIssuanceMessage && + ((CouponIssuanceMessage)message).getCouponId().equals(couponId) && + ((CouponIssuanceMessage)message).getUserId().equals(userId) + )) + ); + } + + @Test + void testCreateUserAndCoupon_NullCouponId() { + // Arrange + Long userId = 1L; + + // Act & Assert + UserCouponValidationException exception = assertThrows( + UserCouponValidationException.class, + () -> rabbitMQUserAndCouponService.createUserAndCoupon(null, userId) + ); + + ErrorStatus errorStatus = exception.getErrorStatus(); + assertEquals("쿠폰아이디와 사용자아이디가 필요합니다.", errorStatus.getMessage()); + assertEquals(HttpStatus.BAD_REQUEST, errorStatus.getStatus()); + assertNotNull(errorStatus.getTimestamp()); + } + + @Test + void testCreateUserAndCoupon_NullUserId() { + // Arrange + Long couponId = 1L; + + // Act & Assert + UserCouponValidationException exception = assertThrows( + UserCouponValidationException.class, + () -> rabbitMQUserAndCouponService.createUserAndCoupon(couponId, null) + ); + + ErrorStatus errorStatus = exception.getErrorStatus(); + assertEquals("쿠폰아이디와 사용자아이디가 필요합니다.", errorStatus.getMessage()); + assertEquals(HttpStatus.BAD_REQUEST, errorStatus.getStatus()); + assertNotNull(errorStatus.getTimestamp()); + } + + @Test + void testCreateUserAndCoupon_NullCouponIdAndUserId() { + // Act & Assert + UserCouponValidationException exception = assertThrows( + UserCouponValidationException.class, + () -> rabbitMQUserAndCouponService.createUserAndCoupon(null, null) + ); + + ErrorStatus errorStatus = exception.getErrorStatus(); + assertEquals("쿠폰아이디와 사용자아이디가 필요합니다.", errorStatus.getMessage()); + assertEquals(HttpStatus.BAD_REQUEST, errorStatus.getStatus()); + assertNotNull(errorStatus.getTimestamp()); + } +} \ No newline at end of file diff --git a/src/test/java/com/nhnacademy/bookstorecoupon/userandcoupon/service/impl/UserAndCouponServiceImplTest.java b/src/test/java/com/nhnacademy/bookstorecoupon/userandcoupon/service/impl/UserAndCouponServiceImplTest.java new file mode 100644 index 0000000..f6fc661 --- /dev/null +++ b/src/test/java/com/nhnacademy/bookstorecoupon/userandcoupon/service/impl/UserAndCouponServiceImplTest.java @@ -0,0 +1,410 @@ +package com.nhnacademy.bookstorecoupon.userandcoupon.service.impl; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Optional; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import com.nhnacademy.bookstorecoupon.bookcoupon.repository.BookCouponRepository; +import com.nhnacademy.bookstorecoupon.categorycoupon.repository.CategoryCouponRepository; +import com.nhnacademy.bookstorecoupon.couponpolicy.domain.entity.CouponPolicy; +import com.nhnacademy.bookstorecoupon.couponpolicy.exception.CouponPolicyNotFoundException; +import com.nhnacademy.bookstorecoupon.couponpolicy.repository.CouponPolicyRepository; +import com.nhnacademy.bookstorecoupon.global.exception.payload.ErrorStatus; +import com.nhnacademy.bookstorecoupon.userandcoupon.domain.dto.response.BirthdayCouponTargetResponse; +import com.nhnacademy.bookstorecoupon.userandcoupon.domain.dto.response.GetBookByOrderCouponResponse; +import com.nhnacademy.bookstorecoupon.userandcoupon.domain.dto.response.UserAndCouponResponseDTO; +import com.nhnacademy.bookstorecoupon.userandcoupon.domain.entity.UserAndCoupon; +import com.nhnacademy.bookstorecoupon.userandcoupon.exception.NotFoundUserAndCouponException; +import com.nhnacademy.bookstorecoupon.userandcoupon.exception.NotFoundUserBirthdayException; +import com.nhnacademy.bookstorecoupon.userandcoupon.feignclient.UserBirthdayFeignClient; +import com.nhnacademy.bookstorecoupon.userandcoupon.repository.UserAndCouponRepository; + +class UserAndCouponServiceImplTest { + + @InjectMocks + private UserAndCouponServiceImpl userAndCouponService; + + @Mock + private UserAndCouponRepository userAndCouponRepository; + + @Mock + private BookCouponRepository bookCouponRepository; + + @Mock + private CategoryCouponRepository categoryCouponRepository; + + @Mock + private UserBirthdayFeignClient userBirthdayFeignClient; + + @Mock + private CouponPolicyRepository couponPolicyRepository; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + void createUserWelcomeCouponIssue_ShouldCreateCoupon_WhenPolicyExists() { + Long userId = 1L; + CouponPolicy couponPolicy = CouponPolicy.builder() + .minOrderPrice(BigDecimal.valueOf(100)) + .salePrice(BigDecimal.valueOf(10)) + .saleRate(null) + .maxSalePrice(null) + .type("welcome") + .build(); + + // 현재 시간 고정 + LocalDateTime now = LocalDateTime.now(); + LocalDateTime expectedExpiredDate = now.plusDays(365); + LocalDateTime expectedIssueDate = now; + + when(couponPolicyRepository.findLatestCouponPolicyByType("welcome")) + .thenReturn(Optional.of(couponPolicy)); + + // 테스트 실행 + userAndCouponService.createUserWelcomeCouponIssue(userId); + + // 검증할 객체의 필드 값 + UserAndCoupon expectedUserAndCoupon = UserAndCoupon.builder() + .couponPolicy(couponPolicy) + .userId(userId) + .isUsed(false) + .expiredDate(expectedExpiredDate) + .issueDate(expectedIssueDate) + .build(); + + // Mockito Argument Matcher 사용 + verify(userAndCouponRepository, times(1)).save(argThat(userAndCoupon -> + userAndCoupon.getCouponPolicy().equals(expectedUserAndCoupon.getCouponPolicy()) && + userAndCoupon.getUserId().equals(expectedUserAndCoupon.getUserId()) && + !userAndCoupon.getIsUsed() && + userAndCoupon.getExpiredDate().isAfter(expectedExpiredDate.minusSeconds(1)) && + userAndCoupon.getExpiredDate().isBefore(expectedExpiredDate.plusSeconds(1)) && + userAndCoupon.getIssueDate().isAfter(expectedIssueDate.minusSeconds(1)) && + userAndCoupon.getIssueDate().isBefore(expectedIssueDate.plusSeconds(1)) + )); + } + + @Test + void createUserWelcomeCouponIssue_ShouldThrowException_WhenPolicyNotFound() { + Long userId = 1L; + + when(couponPolicyRepository.findLatestCouponPolicyByType("welcome")) + .thenReturn(Optional.empty()); + + CouponPolicyNotFoundException exception = assertThrows( + CouponPolicyNotFoundException.class, + () -> userAndCouponService.createUserWelcomeCouponIssue(userId) + ); + + assertEquals("최신 웰컴쿠폰 정책을 찾을 수 없습니다.", exception.getErrorStatus().getMessage()); + } + + + // @Test + // void findExpiredCoupons_ShouldUpdateExpiredCoupons() { + // // 현재 시간 고정 + // LocalDateTime now = LocalDateTime.now(); + // UserAndCoupon expiredCoupon = UserAndCoupon.builder() + // .couponPolicy(CouponPolicy.builder() + // .minOrderPrice(BigDecimal.valueOf(100)) + // .salePrice(BigDecimal.valueOf(100)) + // .maxSalePrice(null) + // .saleRate(null) + // .type("welcome") + // .build()) + // .userId(1L) + // .isUsed(false) + // .expiredDate(now.minusDays(1)) + // .issueDate(now.minusDays(10)) + // .build(); + // + // // Stub the repository method to return the list with the expiredCoupon + // when(userAndCouponRepository.findByExpiredDateBeforeAndIsUsedIsFalse(now)) + // .thenReturn(Collections.singletonList(expiredCoupon)); + // + // // Call the method under test + // userAndCouponService.findExpiredCoupons(); + // + // // Verify that save was called with the updated coupon + // verify(userAndCouponRepository, times(1)).save(argThat(coupon -> + // coupon.getIsUsed() == true && + // coupon.getExpiredDate().isBefore(now) && + // coupon.getIssueDate().isBefore(now) + // )); + // } + @Test + void issueBirthdayCoupon_ShouldIssueCoupons_WhenUsersExist() { + LocalDate today = LocalDate.now(); + BirthdayCouponTargetResponse user = new BirthdayCouponTargetResponse(1L, today); + List birthdayList = Collections.singletonList(user); + + // 현재 시간 고정 + LocalDateTime now = LocalDateTime.now(); + LocalDateTime expectedExpiredDate = now.plusDays(365); + LocalDateTime expectedIssueDate = now; + + + CouponPolicy couponPolicy = CouponPolicy.builder() + .minOrderPrice(BigDecimal.valueOf(100)) + .salePrice(BigDecimal.valueOf(10)) + .saleRate(null) + .maxSalePrice(null) + .type("birthday") + .build(); + + when(userBirthdayFeignClient.getUsersWithBirthday(today)) + .thenReturn(ResponseEntity.ok(birthdayList)); + when(couponPolicyRepository.findLatestCouponPolicyByType("birthday")) + .thenReturn(Optional.of(couponPolicy)); + + userAndCouponService.issueBirthdayCoupon(); + + UserAndCoupon expectedUserAndCoupon = UserAndCoupon.builder() + .couponPolicy(couponPolicy) + .userId(user.userId()) + .isUsed(false) + .expiredDate(expectedExpiredDate) + .issueDate(expectedIssueDate) + .build(); + + + + verify(userAndCouponRepository, times(1)).save(argThat(userAndCoupon -> + userAndCoupon.getCouponPolicy().equals(expectedUserAndCoupon.getCouponPolicy()) && + userAndCoupon.getUserId().equals(expectedUserAndCoupon.getUserId()) && + !userAndCoupon.getIsUsed() && + userAndCoupon.getExpiredDate().isAfter(expectedExpiredDate.minusSeconds(1)) && + userAndCoupon.getExpiredDate().isBefore(expectedExpiredDate.plusSeconds(1)) && + userAndCoupon.getIssueDate().isAfter(expectedIssueDate.minusSeconds(1)) && + userAndCoupon.getIssueDate().isBefore(expectedIssueDate.plusSeconds(1)) + )); + } + + @Test + public void testIssueBirthdayCoupon_whenNoCouponPolicy() { + // Given + LocalDate today = LocalDate.now(); + BirthdayCouponTargetResponse user1 = new BirthdayCouponTargetResponse(1L, today); + List birthdayList = List.of(user1); + + when(userBirthdayFeignClient.getUsersWithBirthday(today)).thenReturn(new ResponseEntity<>(birthdayList, HttpStatus.OK)); + when(couponPolicyRepository.findLatestCouponPolicyByType("birthday")).thenReturn(Optional.empty()); + + // When + CouponPolicyNotFoundException exception = assertThrows(CouponPolicyNotFoundException.class, () -> { + userAndCouponService.issueBirthdayCoupon(); + }); + + // Then + ErrorStatus errorStatus = exception.getErrorStatus(); + assertEquals("최신 생일쿠폰 정책을 찾을 수 없습니다.", errorStatus.getMessage()); + assertEquals(HttpStatus.NOT_FOUND, errorStatus.getStatus()); + assertNotNull(errorStatus.getTimestamp()); + } + + @Test + public void testIssueBirthdayCoupon_whenNoUsersWithBirthday() { + // Given + LocalDate today = LocalDate.now(); + when(userBirthdayFeignClient.getUsersWithBirthday(today)).thenReturn(new ResponseEntity<>(null, HttpStatus.NOT_FOUND)); + + // When & Then + NotFoundUserBirthdayException exception = assertThrows(NotFoundUserBirthdayException.class, () -> { + userAndCouponService.issueBirthdayCoupon(); + }); + ErrorStatus errorStatus = exception.getErrorStatus(); + assertEquals("유저의 생일리스트가 존재하지 않습니다.", errorStatus.getMessage()); + assertEquals(HttpStatus.NOT_FOUND, errorStatus.getStatus()); + assertNotNull(errorStatus.getTimestamp()); + } + + @Test + void getAllUsersAndCouponsByUserPaging_ShouldReturnPagedResult() { + Long userId = 1L; + Pageable pageable = PageRequest.of(0, 10); + UserAndCouponResponseDTO responseDTO = new UserAndCouponResponseDTO( + 1L, userId, null, false, LocalDateTime.now(), LocalDateTime.now(), + BigDecimal.valueOf(100), BigDecimal.valueOf(10), null, null, "book", + true, 1L, "Book Title", null, null); + + Page page = new PageImpl<>(Collections.singletonList(responseDTO)); + + when(userAndCouponRepository.findAllByUserPaging(pageable, userId, new HashMap<>(), new HashMap<>())) + .thenReturn(page); + + Page result = userAndCouponService.getAllUsersAndCouponsByUserPaging(userId, pageable); + + assertEquals(1, result.getTotalElements()); + assertEquals(responseDTO, result.getContent().getFirst()); + } + + @Test + void getAllUsersAndCouponsByManagerPaging_ShouldReturnPagedResult() { + Pageable pageable = PageRequest.of(0, 10); + String type = "welcome"; + Long userId = 1L; + UserAndCouponResponseDTO responseDTO = new UserAndCouponResponseDTO( + 1L, userId, null, false, LocalDateTime.now(), LocalDateTime.now(), + BigDecimal.valueOf(100), BigDecimal.valueOf(10), null, null, "book", + true, 1L, "Book Title", null, null); + + Page page = new PageImpl<>(Collections.singletonList(responseDTO)); + + when(userAndCouponRepository.findAllByManagerPaging(pageable, type, userId, new HashMap<>(), new HashMap<>())) + .thenReturn(page); + + Page result = userAndCouponService.getAllUsersAndCouponsByManagerPaging(pageable, type, userId); + + assertEquals(1, result.getTotalElements()); + assertEquals(responseDTO, result.getContent().getFirst()); + } + + @Test + void findCouponByOrder_ShouldReturnCoupons() { + Long userId = 1L; + List bookIds = Collections.singletonList(1L); + List categoryIds = Collections.singletonList(1L); + BigDecimal bookPrice = BigDecimal.valueOf(50); + UserAndCouponResponseDTO responseDTO = new UserAndCouponResponseDTO( + 1L, userId, null, false, LocalDateTime.now(), LocalDateTime.now(), + BigDecimal.valueOf(100), BigDecimal.valueOf(10), null, null, "book", + true, 1L, "Book Title", null, null); + + when(userAndCouponRepository.findCouponByOrder(userId, new HashMap<>(), new HashMap<>(), bookIds, categoryIds, bookPrice)) + .thenReturn(Collections.singletonList(responseDTO)); + + List result = userAndCouponService.findCouponByOrder(userId, bookIds, categoryIds, bookPrice); + + assertEquals(1, result.size()); + assertEquals(responseDTO, result.getFirst()); + } + + + + + + + + @Test + void findCouponByCartOrder_ShouldReturnCoupons() { + Long userId = 1L; + List bookDetails = Collections.singletonList( + new GetBookByOrderCouponResponse(1L, BigDecimal.valueOf(50), List.of(1L)) + ); + UserAndCouponResponseDTO responseDTO = new UserAndCouponResponseDTO( + 1L, userId, null, false, LocalDateTime.now(), LocalDateTime.now(), + BigDecimal.valueOf(100), BigDecimal.valueOf(10), null, null, "book", + true, 1L, "Book Title", null, null); + + when(userAndCouponRepository.findCouponByCartOrder(userId, new HashMap<>(), new HashMap<>(), bookDetails)) + .thenReturn(Collections.singletonList(responseDTO)); + + List result = userAndCouponService.findCouponByCartOrder(userId, bookDetails); + + assertEquals(1, result.size()); + assertEquals(responseDTO, result.getFirst()); + } + + // @Test + // void updateCouponAfterPayment_ShouldUpdateCoupon() { + // Long userAndCouponId = 1L; + // UserAndCoupon userAndCoupon = UserAndCoupon.builder() + // .couponPolicy(CouponPolicy.builder().minOrderPrice(BigDecimal.valueOf(100)).salePrice(BigDecimal.valueOf(100)).maxSalePrice(null).saleRate(null).type("welcome").build()) + // .userId(1L) + // .isUsed(false) + // .expiredDate(LocalDateTime.now().plusDays(30)) + // .issueDate(LocalDateTime.now().minusDays(10)) + // .build(); + // + // when(userAndCouponRepository.findById(userAndCouponId)).thenReturn(Optional.of(userAndCoupon)); + // + // userAndCouponService.updateCouponAfterPayment(userAndCouponId); + // + // verify(userAndCouponRepository, times(1)).save(argThat(coupon -> + // coupon.getId().equals(userAndCouponId) && + // coupon.getIsUsed() && + // coupon.getUsedDate().isAfter(LocalDateTime.now().minusMinutes(1)) + // )); + // } + + @Test + void updateCouponAfterPayment_ShouldThrowException_WhenCouponNotFound() { + Long userAndCouponId = 1L; + + when(userAndCouponRepository.findById(userAndCouponId)).thenReturn(Optional.empty()); + + NotFoundUserAndCouponException exception = assertThrows( + NotFoundUserAndCouponException.class, + () -> userAndCouponService.updateCouponAfterPayment(userAndCouponId) + ); + + assertEquals("유저의 쿠폰이 존재하지 않습니다.", exception.getErrorStatus().getMessage()); + } + + // @Test + // void findUserAndCouponsById_ShouldReturnCouponDetails() { + // Long couponId = 1L; + // CouponPolicy couponPolicy = CouponPolicy.builder() + // .minOrderPrice(BigDecimal.valueOf(100)) + // .salePrice(BigDecimal.valueOf(10)) + // .saleRate(null) + // .maxSalePrice(null) + // .type("welcome") + // .build(); + // + // UserAndCoupon userAndCoupon = UserAndCoupon.builder() + // .couponPolicy(couponPolicy) + // .userId(1L) + // .isUsed(false) + // .expiredDate(LocalDateTime.now().plusDays(30)) + // .issueDate(LocalDateTime.now().minusDays(10)) + // .build(); + // + // when(userAndCouponRepository.findById(couponId)).thenReturn(Optional.of(userAndCoupon)); + // + // UserAndCouponOrderResponseDTO responseDTO = userAndCouponService.findUserAndCouponsById(couponId); + // + // assertEquals(couponId, responseDTO.id()); + // assertEquals(couponPolicy.getMinOrderPrice(), responseDTO.minOrderPrice()); + // assertEquals(couponPolicy.getSalePrice(), responseDTO.salePrice()); + // assertEquals(couponPolicy.getSaleRate(), responseDTO.saleRate()); + // assertEquals(couponPolicy.getMaxSalePrice(), responseDTO.maxSalePrice()); + // assertEquals(couponPolicy.getType(), responseDTO.type()); + // } + + @Test + void findUserAndCouponsById_ShouldThrowException_WhenCouponNotFound() { + Long couponId = 1L; + + when(userAndCouponRepository.findById(couponId)).thenReturn(Optional.empty()); + + NotFoundUserAndCouponException exception = assertThrows( + NotFoundUserAndCouponException.class, + () -> userAndCouponService.findUserAndCouponsById(couponId) + ); + + assertEquals("유저의 쿠폰이 존재하지 않습니다.", exception.getErrorStatus().getMessage()); + } +} \ No newline at end of file diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index d93dfe7..00de1f6 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -1,3 +1,4 @@ + spring: rabbitmq: host: 133.186.241.167 @@ -17,17 +18,10 @@ spring: driver-class-name: org.h2.Driver username: sa type: org.apache.commons.dbcp2.BasicDataSource - dbcp2: - initial-size: 50 # 초기로 생성할 연결의 수를 설정합니다. - max-total: 50 # 풀에서 유지할 수 있는 최대 연결 수를 설정합니다. - max-idle: 50 # 유휴 상태로 유지할 최대 연결 수를 설정합니다. - min-idle: 50 # 유휴 상태로 유지할 최소 연결 수를 설정합니다. - test-on-borrow: true # 풀에서 연결을 빌릴 때마다 해당 연결이 유효한지 검증할지 여부를 설정합니다. - validation-query: SELECT 1 # 연결 유효성을 검사하기 위해 실행할 SQL 쿼리를 설정합니다. jpa: hibernate: - ddl-auto: update + ddl-auto: create-drop show-sql: true h2: console: @@ -37,3 +31,5 @@ spring: remote-addr: 127.0.0.1 + +