diff --git a/src/main/java/com/moabam/api/application/coupon/CouponManageService.java b/src/main/java/com/moabam/api/application/coupon/CouponManageService.java index 67e3bac7..3cda6f9a 100644 --- a/src/main/java/com/moabam/api/application/coupon/CouponManageService.java +++ b/src/main/java/com/moabam/api/application/coupon/CouponManageService.java @@ -16,6 +16,7 @@ import com.moabam.global.common.util.ClockHolder; import com.moabam.global.error.exception.BadRequestException; import com.moabam.global.error.exception.ConflictException; +import com.moabam.global.error.exception.NotFoundException; import com.moabam.global.error.model.ErrorMessage; import lombok.RequiredArgsConstructor; @@ -75,7 +76,7 @@ public void registerQueue(String couponName, Long memberId) { couponManageRepository.addIfAbsentQueue(couponName, memberId, registerTime); } - public void deleteQueue(String couponName) { + public void delete(String couponName) { couponManageRepository.deleteQueue(couponName); couponManageRepository.deleteCount(couponName); } @@ -83,7 +84,7 @@ public void deleteQueue(String couponName) { private void validateRegisterQueue(String couponName, Long memberId) { LocalDate now = clockHolder.date(); Coupon coupon = couponRepository.findByNameAndStartAt(couponName, now) - .orElseThrow(() -> new BadRequestException(ErrorMessage.INVALID_COUPON_PERIOD)); + .orElseThrow(() -> new NotFoundException(ErrorMessage.INVALID_COUPON_PERIOD)); if (couponManageRepository.hasValue(couponName, memberId)) { throw new ConflictException(ErrorMessage.CONFLICT_COUPON_ISSUE); diff --git a/src/main/java/com/moabam/api/application/coupon/CouponService.java b/src/main/java/com/moabam/api/application/coupon/CouponService.java index 43b289fb..3a765dff 100644 --- a/src/main/java/com/moabam/api/application/coupon/CouponService.java +++ b/src/main/java/com/moabam/api/application/coupon/CouponService.java @@ -60,7 +60,7 @@ public void delete(Long couponId, Role role) { .orElseThrow(() -> new NotFoundException(ErrorMessage.NOT_FOUND_COUPON)); couponRepository.delete(coupon); - couponManageService.deleteQueue(coupon.getName()); + couponManageService.delete(coupon.getName()); } @Transactional diff --git a/src/main/java/com/moabam/api/domain/coupon/repository/CouponManageRepository.java b/src/main/java/com/moabam/api/domain/coupon/repository/CouponManageRepository.java index 44e2c150..0381f236 100644 --- a/src/main/java/com/moabam/api/domain/coupon/repository/CouponManageRepository.java +++ b/src/main/java/com/moabam/api/domain/coupon/repository/CouponManageRepository.java @@ -58,7 +58,13 @@ public int rankQueue(String couponName, Long memberId) { public int getCount(String couponName) { String couponCountKey = String.format(COUPON_COUNT_KEY, requireNonNull(couponName)); - return Integer.parseInt(valueRedisRepository.get(couponCountKey)); + String count = valueRedisRepository.get(couponCountKey); + + if (isNull(count)) { + return 0; + } + + return Integer.parseInt(count); } public void increase(String couponName, long count) { diff --git a/src/main/resources/static/docs/coupon.html b/src/main/resources/static/docs/coupon.html index d7435ce2..af8593f3 100644 --- a/src/main/resources/static/docs/coupon.html +++ b/src/main/resources/static/docs/coupon.html @@ -498,7 +498,7 @@

쿠폰 삭제

요청

-
DELETE /admins/coupons/33 HTTP/1.1
+
DELETE /admins/coupons/36 HTTP/1.1
 Host: localhost:8080
@@ -526,7 +526,7 @@

특정 쿠폰 조회

요청

-
GET /coupons/21 HTTP/1.1
+
GET /coupons/24 HTTP/1.1
 Host: localhost:8080
@@ -543,7 +543,7 @@

응답

Content-Length: 201 { - "id" : 21, + "id" : 24, "adminName" : "ID : 1", "name" : "couponName", "description" : "", @@ -593,7 +593,7 @@

응답

Content-Length: 202 [ { - "id" : 22, + "id" : 25, "adminName" : "ID : 1", "name" : "coupon1", "description" : "", @@ -630,17 +630,17 @@

요청

응답

-
HTTP/1.1 400 Bad Request
+
HTTP/1.1 409 Conflict
 Access-Control-Allow-Origin:
 Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
 Access-Control-Allow-Headers: Origin, Accept, Access-Control-Request-Method, Access-Control-Request-Headers, X-Requested-With,Content-Type, Referer
 Access-Control-Allow-Credentials: true
 Access-Control-Max-Age: 3600
 Content-Type: application/json
-Content-Length: 64
+Content-Length: 63
 
 {
-  "message" : "쿠폰 발급 가능 기간이 아닙니다."
+  "message" : "이미 쿠폰 발급에 성공했습니다!"
 }
diff --git a/src/test/java/com/moabam/api/application/coupon/CouponManageServiceTest.java b/src/test/java/com/moabam/api/application/coupon/CouponManageServiceTest.java index baa32d07..d209cdfe 100644 --- a/src/test/java/com/moabam/api/application/coupon/CouponManageServiceTest.java +++ b/src/test/java/com/moabam/api/application/coupon/CouponManageServiceTest.java @@ -24,7 +24,7 @@ import com.moabam.api.domain.coupon.repository.CouponRepository; import com.moabam.api.domain.coupon.repository.CouponWalletRepository; import com.moabam.global.common.util.ClockHolder; -import com.moabam.global.error.exception.BadRequestException; +import com.moabam.global.error.exception.NotFoundException; import com.moabam.global.error.model.ErrorMessage; import com.moabam.support.common.FilterProcessExtension; import com.moabam.support.fixture.CouponFixture; @@ -140,7 +140,7 @@ void registerQueue_No_BadRequestException() { // When & Then assertThatThrownBy(() -> couponManageService.registerQueue("couponName", 1L)) - .isInstanceOf(BadRequestException.class) + .isInstanceOf(NotFoundException.class) .hasMessage(ErrorMessage.INVALID_COUPON_PERIOD.getMessage()); } @@ -151,7 +151,7 @@ void deleteQueue_success() { String couponName = "couponName"; // When - couponManageService.deleteQueue(couponName); + couponManageService.delete(couponName); // Then verify(couponManageRepository).deleteQueue(couponName); @@ -166,7 +166,7 @@ void deleteQueue_NullPointerException() { .deleteQueue(any(String.class)); // When & Then - assertThatThrownBy(() -> couponManageService.deleteQueue("null")) + assertThatThrownBy(() -> couponManageService.delete("null")) .isInstanceOf(NullPointerException.class); } } diff --git a/src/test/java/com/moabam/api/application/coupon/CouponServiceTest.java b/src/test/java/com/moabam/api/application/coupon/CouponServiceTest.java index a8f37413..b44d6f8c 100644 --- a/src/test/java/com/moabam/api/application/coupon/CouponServiceTest.java +++ b/src/test/java/com/moabam/api/application/coupon/CouponServiceTest.java @@ -183,7 +183,7 @@ void delete_success() { // Then verify(couponRepository).delete(coupon); - verify(couponManageService).deleteQueue(any(String.class)); + verify(couponManageService).delete(any(String.class)); } @DisplayName("권한 없는 사용자가 쿠폰을 삭제한다. - NotFoundException") diff --git a/src/test/java/com/moabam/api/presentation/CouponControllerTest.java b/src/test/java/com/moabam/api/presentation/CouponControllerTest.java index fd968940..65f25723 100644 --- a/src/test/java/com/moabam/api/presentation/CouponControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/CouponControllerTest.java @@ -35,6 +35,7 @@ import com.moabam.api.domain.member.repository.MemberRepository; import com.moabam.api.dto.coupon.CouponStatusRequest; import com.moabam.api.dto.coupon.CreateCouponRequest; +import com.moabam.api.infrastructure.redis.ValueRedisRepository; import com.moabam.api.infrastructure.redis.ZSetRedisRepository; import com.moabam.global.common.util.ClockHolder; import com.moabam.global.error.model.ErrorMessage; @@ -73,6 +74,9 @@ class CouponControllerTest extends WithoutFilterSupporter { @MockBean ZSetRedisRepository zSetRedisRepository; + @MockBean + ValueRedisRepository valueRedisRepository; + @WithMember(role = Role.ADMIN) @DisplayName("POST - 쿠폰을 성공적으로 발행한다. - Void") @Test @@ -420,9 +424,31 @@ void use_BadRequestException() throws Exception { } @WithMember - @DisplayName("POST - 발급 가능 날짜가 아닌 쿠폰에 발급 요청을 한다. (Not found) - BadRequestException") + @DisplayName("POST - 쿠폰 발급을 성공적으로 한다. - Void") @Test - void registerQueue_Zero_StartAt_BadRequestException() throws Exception { + void registerQueue_success() throws Exception { + // Given + Coupon couponFixture = CouponFixture.coupon("CouponName", 2, 1); + Coupon coupon = couponRepository.save(couponFixture); + + given(clockHolder.date()).willReturn(LocalDate.of(2023, 2, 1)); + given(zSetRedisRepository.score(anyString(), anyLong())).willReturn(null); + given(zSetRedisRepository.size(anyString())).willReturn((long)(coupon.getMaxCount() - 1)); + + // When & Then + mockMvc.perform(post("/coupons") + .param("couponName", coupon.getName())) + .andDo(print()) + .andDo(document("coupons", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()))) + .andExpect(status().isOk()); + } + + @WithMember + @DisplayName("POST - 발급 가능 날짜가 아닌 쿠폰에 발급 요청을 한다. - NotFoundException") + @Test + void registerQueue_NotFoundException() throws Exception { // Given Coupon couponFixture = CouponFixture.coupon(); Coupon coupon = couponRepository.save(couponFixture); @@ -437,39 +463,45 @@ void registerQueue_Zero_StartAt_BadRequestException() throws Exception { preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), ErrorSnippet.ERROR_MESSAGE_RESPONSE)) - .andExpect(status().isBadRequest()) + .andExpect(status().isNotFound()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.message").value(ErrorMessage.INVALID_COUPON_PERIOD.getMessage())); } @WithMember - @DisplayName("POST - 발급 가능 날짜가 아닌 쿠폰에 발급 요청을 한다. (Not equals) - BadRequestException") + @DisplayName("POST - 동일한 쿠폰 이벤트에 중복으로 요청한다. - ConflictException") @Test - void registerQueue_Not_StartAt_BadRequestException() throws Exception { + void registerQueue_ConflictException() throws Exception { // Given - given(clockHolder.date()).willReturn(LocalDate.of(2022, 2, 1)); + Coupon coupon = couponRepository.save(CouponFixture.coupon()); + + given(clockHolder.date()).willReturn(LocalDate.of(2023, 2, 1)); + given(zSetRedisRepository.score(anyString(), anyLong())).willReturn(7.0); // When & Then mockMvc.perform(post("/coupons") - .param("couponName", "not start couponName")) + .param("couponName", coupon.getName())) .andDo(print()) .andDo(document("coupons", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), ErrorSnippet.ERROR_MESSAGE_RESPONSE)) - .andExpect(status().isBadRequest()) + .andExpect(status().isConflict()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(jsonPath("$.message").value(ErrorMessage.INVALID_COUPON_PERIOD.getMessage())); + .andExpect(jsonPath("$.message").value(ErrorMessage.CONFLICT_COUPON_ISSUE.getMessage())); } @WithMember - @DisplayName("POST - 존재하지 않는 쿠폰에 발급 요청을 한다. - NotFoundException") + @DisplayName("POST - 선착순 이벤트가 마감된 쿠폰에 발급 요청을 한다. - BadRequestException") @Test - void registerQueue_NotFoundException() throws Exception { + void registerQueue_BadRequestException() throws Exception { // Given - Coupon coupon = CouponFixture.coupon("Not found couponName", 2, 1); + Coupon couponFixture = CouponFixture.coupon(); + Coupon coupon = couponRepository.save(couponFixture); given(clockHolder.date()).willReturn(LocalDate.of(2023, 2, 1)); + given(zSetRedisRepository.score(anyString(), anyLong())).willReturn(null); + given(zSetRedisRepository.size(anyString())).willReturn((long)(coupon.getMaxCount())); // When & Then mockMvc.perform(post("/coupons") @@ -481,6 +513,6 @@ void registerQueue_NotFoundException() throws Exception { ErrorSnippet.ERROR_MESSAGE_RESPONSE)) .andExpect(status().isBadRequest()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(jsonPath("$.message").value(ErrorMessage.INVALID_COUPON_PERIOD.getMessage())); + .andExpect(jsonPath("$.message").value(ErrorMessage.INVALID_COUPON_STOCK_END.getMessage())); } }