Skip to content

Commit 37556d7

Browse files
committed
test #223: 쿠폰 api 테스트
1 parent f64e91d commit 37556d7

5 files changed

Lines changed: 209 additions & 5 deletions

File tree

src/docs/asciidoc/_coupon.adoc

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
== 쿠폰 (Coupons) API
2+
3+
=== 1. 쿠폰 생성
4+
`POST /coupons`
5+
6+
요청 본문:
7+
include::{snippets}/coupon-create/request-fields.adoc[]
8+
9+
응답:
10+
include::{snippets}/coupon-create/http-response.adoc[]
11+
12+
13+
=== 2. 쿠폰 조회
14+
`GET /coupons`
15+
16+
응답 본문:
17+
include::{snippets}/coupon-list/response-fields.adoc[]
18+
19+
응답:
20+
include::{snippets}/coupon-list/http-response.adoc[]
21+
22+
23+
=== 7. 쿠폰 삭제
24+
`DELETE /coupons`
25+
26+
응답:
27+
include::{snippets}/coupon-delete/http-response.adoc[]

src/docs/asciidoc/index.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
include::_member.adoc[]
1010
include::_manager.adoc[]
11+
include::_coupon.adoc[]
1112

1213
// include::_product.adoc[]
1314

src/main/java/com/irum/come2us/domain/coupon/presentation/controller/CouponController.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,12 @@ public ResponseEntity<CouponGenerateRequest> createCoupon(
3232
public ResponseEntity<List<CouponResponse>> getCoupon(
3333
@AuthenticationPrincipal UserDetails userDetails) {
3434
Long memberId = Long.parseLong(userDetails.getUsername());
35-
List<CouponResponse> coupons = couponService.getCouponByMember(memberId);
36-
return ResponseEntity.ok(coupons);
35+
return ResponseEntity.ok(couponService.getCouponByMember(memberId));
3736
}
3837

39-
@DeleteMapping("/{coupon-id}")
38+
@DeleteMapping("/{couponId}")
4039
public ResponseEntity<Void> deleteCoupon(
41-
@PathVariable("coupon-id") UUID couponId,
40+
@PathVariable("couponId") UUID couponId,
4241
@AuthenticationPrincipal UserDetails userDetails) {
4342

4443
Long memberId = Long.parseLong(userDetails.getUsername());

src/main/java/com/irum/come2us/domain/coupon/presentation/dto/response/CouponResponse.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
package com.irum.come2us.domain.coupon.presentation.dto.response;
22

3+
import com.fasterxml.jackson.annotation.JsonFormat;
34
import com.irum.come2us.domain.coupon.domain.entity.Coupon;
45
import java.time.LocalDateTime;
56
import java.util.UUID;
67

7-
public record CouponResponse(UUID id, String name, int discountAmount, LocalDateTime expiration) {
8+
public record CouponResponse(
9+
UUID id,
10+
String name,
11+
int discountAmount,
12+
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") // 명시적 포맷 추가
13+
// 또는 요청 DTO와 동일하게 @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
14+
LocalDateTime expiration) {
815
public static CouponResponse from(Coupon coupon) {
916
return new CouponResponse(
1017
coupon.getId(),
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
package com.irum.come2us.domain.coupon;
2+
3+
import static org.hamcrest.Matchers.hasSize;
4+
import static org.mockito.ArgumentMatchers.any;
5+
import static org.mockito.ArgumentMatchers.anyLong;
6+
import static org.mockito.Mockito.*;
7+
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
8+
import static org.springframework.restdocs.payload.PayloadDocumentation.*;
9+
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
10+
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
11+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
12+
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
13+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
14+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
15+
16+
import com.fasterxml.jackson.databind.ObjectMapper;
17+
import com.irum.come2us.domain.coupon.application.service.CouponService;
18+
import com.irum.come2us.domain.coupon.presentation.controller.CouponController;
19+
import com.irum.come2us.domain.coupon.presentation.dto.request.CouponGenerateRequest;
20+
import com.irum.come2us.domain.coupon.presentation.dto.response.CouponResponse;
21+
import com.irum.come2us.global.config.SecurityTestConfig;
22+
import com.irum.come2us.global.presentation.advice.exception.CommonException;
23+
import com.irum.come2us.global.presentation.advice.exception.errorcode.CouponErrorCode;
24+
import java.time.LocalDateTime;
25+
import java.util.List;
26+
import java.util.UUID;
27+
import org.junit.jupiter.api.DisplayName;
28+
import org.junit.jupiter.api.Test;
29+
import org.mockito.Mockito;
30+
import org.springframework.beans.factory.annotation.Autowired;
31+
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
32+
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
33+
import org.springframework.boot.test.context.TestConfiguration;
34+
import org.springframework.context.annotation.Bean;
35+
import org.springframework.context.annotation.Import;
36+
import org.springframework.context.annotation.Primary;
37+
import org.springframework.test.web.servlet.MockMvc;
38+
39+
@WebMvcTest(CouponController.class)
40+
@AutoConfigureRestDocs
41+
@Import({SecurityTestConfig.class, CouponControllerTest.TestConfig.class})
42+
public class CouponControllerTest {
43+
@Autowired private MockMvc mockMvc;
44+
@Autowired private CouponService couponService;
45+
@Autowired private ObjectMapper objectMapper;
46+
47+
@TestConfiguration
48+
static class TestConfig {
49+
50+
@Bean
51+
@Primary
52+
public CouponService couponService() {
53+
return Mockito.mock(CouponService.class);
54+
}
55+
}
56+
57+
@Test
58+
@DisplayName("쿠폰 생성 API")
59+
void couponCreateApiTest() throws Exception {
60+
61+
// Given
62+
CouponGenerateRequest request =
63+
new CouponGenerateRequest(
64+
"기간 한정 5000원 할인쿠폰", 5000, LocalDateTime.of(2028, 10, 10, 12, 12, 12));
65+
String requestJson = objectMapper.writeValueAsString(request);
66+
67+
doNothing().when(couponService).createCoupon(any(CouponGenerateRequest.class), anyLong());
68+
69+
// when
70+
mockMvc.perform(
71+
post("/coupons")
72+
.with(csrf())
73+
.with(user("1"))
74+
.contentType("application/json")
75+
.content(requestJson))
76+
.andExpect(status().isCreated())
77+
.andDo(
78+
document(
79+
"coupon-create",
80+
requestFields(
81+
fieldWithPath("name").description("쿠폰명"),
82+
fieldWithPath("discountAmount").description("할인 금액"),
83+
fieldWithPath("expiration").description("유효기간"))));
84+
}
85+
86+
@Test
87+
@DisplayName("쿠폰 정보 조회 API")
88+
void couponListTest() throws Exception {
89+
// Given
90+
LocalDateTime now = LocalDateTime.of(2028, 10, 10, 12, 12, 12);
91+
CouponResponse coupon1 =
92+
new CouponResponse(
93+
UUID.fromString("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"),
94+
"기간 한정 5000원 할인쿠폰",
95+
5000,
96+
now);
97+
98+
CouponResponse coupon2 =
99+
new CouponResponse(
100+
UUID.fromString("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"),
101+
"신규가입 10% 할인",
102+
10,
103+
now.plusDays(1));
104+
105+
List<CouponResponse> responses = List.of(coupon1, coupon2);
106+
when(couponService.getCouponByMember(anyLong())).thenReturn(responses);
107+
108+
// When & Then
109+
mockMvc.perform(get("/coupons").with(user("1")).accept("application/json"))
110+
.andDo(print())
111+
.andExpect(status().isOk())
112+
.andExpect(jsonPath("$.success").value(true))
113+
.andExpect(jsonPath("$.status").value(200))
114+
.andExpect(jsonPath("$.data").isArray())
115+
.andExpect(jsonPath("$.data", hasSize(2)))
116+
// coupon1
117+
.andExpect(jsonPath("$.data[0].id").value("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"))
118+
.andExpect(jsonPath("$.data[0].name").value("기간 한정 5000원 할인쿠폰"))
119+
.andExpect(jsonPath("$.data[0].discountAmount").value(5000))
120+
.andExpect(jsonPath("$.data[0].expiration").value("2028-10-10 12:12:12"))
121+
// coupon2
122+
.andExpect(jsonPath("$.data[1].id").value("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"))
123+
.andExpect(jsonPath("$.data[1].name").value("신규가입 10% 할인"))
124+
.andExpect(jsonPath("$.data[1].discountAmount").value(10))
125+
.andExpect(jsonPath("$.data[1].expiration").value("2028-10-11 12:12:12"))
126+
.andExpect(jsonPath("$.timestamp").exists())
127+
.andDo(
128+
document(
129+
"coupon-list",
130+
responseFields(
131+
fieldWithPath("success").description("성공 여부"),
132+
fieldWithPath("status").description("HTTP 상태 코드"),
133+
fieldWithPath("data").description("쿠폰 목록"),
134+
fieldWithPath("data[].id").description("쿠폰 ID"),
135+
fieldWithPath("data[].name").description("쿠폰명"),
136+
fieldWithPath("data[].discountAmount").description("할인 금액"),
137+
fieldWithPath("data[].expiration")
138+
.description("만료 일시 (yyyy-MM-dd HH:mm:ss)"),
139+
fieldWithPath("timestamp").description("응답 생성 시각"))));
140+
}
141+
142+
@Test
143+
@DisplayName("쿠폰 삭제 API")
144+
void couponDeleteApiTest() throws Exception {
145+
// Given
146+
UUID couponId = UUID.fromString("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa");
147+
doNothing().when(couponService).deleteCoupon(couponId, 1L);
148+
149+
// When & Then
150+
mockMvc.perform(delete("/coupons/{couponId}", couponId).with(csrf()).with(user("1")))
151+
.andExpect(status().isNoContent())
152+
.andDo(document("coupon-delete"));
153+
154+
Mockito.verify(couponService).deleteCoupon(couponId, 1L);
155+
}
156+
157+
// import static org.mockito.Mockito.doThrow;
158+
159+
@Test
160+
@DisplayName("쿠폰 삭제 API - 쿠폰 없음")
161+
void couponDelete_NotFound() throws Exception {
162+
UUID couponId = UUID.fromString("cccccccc-bbbb-bbbb-bbbb-bbbbbbbbbbbb");
163+
doThrow(new CommonException(CouponErrorCode.COUPON_NOT_FOUND))
164+
.when(couponService)
165+
.deleteCoupon(couponId, 1L);
166+
167+
mockMvc.perform(delete("/coupons/{coupon-id}", couponId).with(csrf()).with(user("1")))
168+
.andExpect(status().isNotFound());
169+
}
170+
}

0 commit comments

Comments
 (0)