Skip to content

Commit b99c7b9

Browse files
committed
test: RankingV1ApiE2ETest 클래스 추가 및 랭킹 API 기능 테스트 구현
1 parent f5a6e25 commit b99c7b9

1 file changed

Lines changed: 211 additions & 0 deletions

File tree

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
package com.loopers.interfaces.api.ranking;
2+
3+
import com.loopers.domain.brand.Brand;
4+
import com.loopers.domain.product.Product;
5+
import com.loopers.fixture.TestFixture;
6+
import com.loopers.interfaces.api.ApiResponse;
7+
import com.loopers.utils.DatabaseCleanUp;
8+
import com.loopers.utils.RedisCleanUp;
9+
import org.junit.jupiter.api.*;
10+
import org.springframework.beans.factory.annotation.Autowired;
11+
import org.springframework.boot.test.context.SpringBootTest;
12+
import org.springframework.boot.test.web.client.TestRestTemplate;
13+
import org.springframework.core.ParameterizedTypeReference;
14+
import org.springframework.data.redis.core.RedisTemplate;
15+
import org.springframework.http.*;
16+
17+
import java.time.LocalDate;
18+
import java.time.format.DateTimeFormatter;
19+
20+
import static org.assertj.core.api.Assertions.assertThat;
21+
import static org.junit.jupiter.api.Assertions.assertAll;
22+
import static org.junit.jupiter.api.Assertions.assertTrue;
23+
24+
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
25+
class RankingV1ApiE2ETest {
26+
27+
private static final String ENDPOINT_RANKINGS = "/api/v1/rankings";
28+
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd");
29+
30+
@Autowired
31+
private TestRestTemplate testRestTemplate;
32+
33+
@Autowired
34+
private DatabaseCleanUp databaseCleanUp;
35+
36+
@Autowired
37+
private RedisCleanUp redisCleanUp;
38+
39+
@Autowired
40+
private TestFixture testFixture;
41+
42+
@Autowired
43+
private RedisTemplate<String, String> redisTemplate;
44+
45+
private Brand savedBrand;
46+
private Product savedProduct1;
47+
private Product savedProduct2;
48+
private Product savedProduct3;
49+
private String todayKey;
50+
51+
@BeforeEach
52+
void setUp() {
53+
// 데이터 초기화
54+
databaseCleanUp.truncateAllTables();
55+
redisCleanUp.truncateAll();
56+
57+
// 테스트 데이터 생성
58+
savedBrand = testFixture.createBrand("Nike");
59+
savedProduct1 = testFixture.createProduct("Air Max", 150000L, 100, savedBrand);
60+
savedProduct2 = testFixture.createProduct("Air Force", 120000L, 100, savedBrand);
61+
savedProduct3 = testFixture.createProduct("Jordan", 200000L, 100, savedBrand);
62+
63+
// Redis에 랭킹 데이터 생성
64+
LocalDate today = LocalDate.now();
65+
todayKey = "ranking:all:" + today.format(DATE_FORMATTER);
66+
67+
redisTemplate.opsForZSet().add(todayKey, savedProduct1.getId().toString(), 10.5);
68+
redisTemplate.opsForZSet().add(todayKey, savedProduct2.getId().toString(), 8.3);
69+
redisTemplate.opsForZSet().add(todayKey, savedProduct3.getId().toString(), 15.2);
70+
}
71+
72+
@AfterEach
73+
void tearDown() {
74+
databaseCleanUp.truncateAllTables();
75+
redisCleanUp.truncateAll();
76+
}
77+
78+
@Nested
79+
@DisplayName("GET /api/v1/rankings")
80+
class GetRankings {
81+
82+
@Test
83+
@DisplayName("랭킹 페이지 조회에 성공하면 상품 정보가 포함된 랭킹 목록을 반환한다")
84+
void shouldReturnRankingPageWithProductInfo() {
85+
// given
86+
String today = LocalDate.now().format(DATE_FORMATTER);
87+
String url = String.format("%s?date=%s&page=0&size=10", ENDPOINT_RANKINGS, today);
88+
89+
// when
90+
ParameterizedTypeReference<ApiResponse<RankingV1Dto.RankingPageResponse>> responseType =
91+
new ParameterizedTypeReference<>() {};
92+
ResponseEntity<ApiResponse<RankingV1Dto.RankingPageResponse>> response =
93+
testRestTemplate.exchange(url, HttpMethod.GET, null, responseType);
94+
95+
// then
96+
assertAll(
97+
() -> assertTrue(response.getStatusCode().is2xxSuccessful()),
98+
() -> assertThat(response.getBody()).isNotNull(),
99+
() -> assertThat(response.getBody().data().rankings()).hasSize(3),
100+
() -> assertThat(response.getBody().data().rankings().get(0).productName()).isEqualTo("Jordan"),
101+
() -> assertThat(response.getBody().data().rankings().get(0).rank()).isEqualTo(1L),
102+
() -> assertThat(response.getBody().data().rankings().get(0).score()).isEqualTo(15.2)
103+
);
104+
}
105+
106+
@Test
107+
@DisplayName("페이지네이션이 정상 동작한다")
108+
void shouldReturnPaginatedResults() {
109+
// given
110+
String today = LocalDate.now().format(DATE_FORMATTER);
111+
String url = String.format("%s?date=%s&page=0&size=2", ENDPOINT_RANKINGS, today);
112+
113+
// when
114+
ParameterizedTypeReference<ApiResponse<RankingV1Dto.RankingPageResponse>> responseType =
115+
new ParameterizedTypeReference<>() {};
116+
ResponseEntity<ApiResponse<RankingV1Dto.RankingPageResponse>> response =
117+
testRestTemplate.exchange(url, HttpMethod.GET, null, responseType);
118+
119+
// then
120+
assertAll(
121+
() -> assertTrue(response.getStatusCode().is2xxSuccessful()),
122+
() -> assertThat(response.getBody().data().rankings()).hasSize(2),
123+
() -> assertThat(response.getBody().data().totalCount()).isEqualTo(3L),
124+
() -> assertThat(response.getBody().data().totalPages()).isEqualTo(2)
125+
);
126+
}
127+
128+
@Test
129+
@DisplayName("date 파라미터가 없으면 오늘 날짜로 조회한다")
130+
void shouldUseCurrentDateWhenDateIsNotProvided() {
131+
// given
132+
String url = String.format("%s?page=0&size=10", ENDPOINT_RANKINGS);
133+
134+
// when
135+
ParameterizedTypeReference<ApiResponse<RankingV1Dto.RankingPageResponse>> responseType =
136+
new ParameterizedTypeReference<>() {};
137+
ResponseEntity<ApiResponse<RankingV1Dto.RankingPageResponse>> response =
138+
testRestTemplate.exchange(url, HttpMethod.GET, null, responseType);
139+
140+
// then
141+
assertAll(
142+
() -> assertTrue(response.getStatusCode().is2xxSuccessful()),
143+
() -> assertThat(response.getBody().data().date()).isEqualTo(LocalDate.now().format(DATE_FORMATTER))
144+
);
145+
}
146+
147+
@Test
148+
@DisplayName("랭킹 데이터가 없는 날짜 조회 시 빈 목록을 반환한다")
149+
void shouldReturnEmptyListWhenNoRankingData() {
150+
// given
151+
String futureDate = LocalDate.now().plusDays(10).format(DATE_FORMATTER);
152+
String url = String.format("%s?date=%s&page=0&size=10", ENDPOINT_RANKINGS, futureDate);
153+
154+
// when
155+
ParameterizedTypeReference<ApiResponse<RankingV1Dto.RankingPageResponse>> responseType =
156+
new ParameterizedTypeReference<>() {};
157+
ResponseEntity<ApiResponse<RankingV1Dto.RankingPageResponse>> response =
158+
testRestTemplate.exchange(url, HttpMethod.GET, null, responseType);
159+
160+
// then
161+
assertAll(
162+
() -> assertTrue(response.getStatusCode().is2xxSuccessful()),
163+
() -> assertThat(response.getBody().data().rankings()).isEmpty(),
164+
() -> assertThat(response.getBody().data().totalCount()).isEqualTo(0L)
165+
);
166+
}
167+
168+
@Test
169+
@DisplayName("유효하지 않은 size 값은 기본값으로 처리된다")
170+
void shouldUseDefaultSizeWhenInvalid() {
171+
// given
172+
String today = LocalDate.now().format(DATE_FORMATTER);
173+
String url = String.format("%s?date=%s&page=0&size=0", ENDPOINT_RANKINGS, today);
174+
175+
// when
176+
ParameterizedTypeReference<ApiResponse<RankingV1Dto.RankingPageResponse>> responseType =
177+
new ParameterizedTypeReference<>() {};
178+
ResponseEntity<ApiResponse<RankingV1Dto.RankingPageResponse>> response =
179+
testRestTemplate.exchange(url, HttpMethod.GET, null, responseType);
180+
181+
// then
182+
assertTrue(response.getStatusCode().is2xxSuccessful() || response.getStatusCode().is4xxClientError());
183+
}
184+
}
185+
186+
@Nested
187+
@DisplayName("GET /api/v1/rankings/top")
188+
class GetTopN {
189+
190+
@Test
191+
@DisplayName("Top-N 랭킹을 조회한다")
192+
void shouldReturnTopN() {
193+
// given
194+
String today = LocalDate.now().format(DATE_FORMATTER);
195+
String url = String.format("%s/top?date=%s&n=2", ENDPOINT_RANKINGS, today);
196+
197+
// when
198+
ParameterizedTypeReference<ApiResponse<RankingV1Dto.TopNResponse>> responseType =
199+
new ParameterizedTypeReference<>() {};
200+
ResponseEntity<ApiResponse<RankingV1Dto.TopNResponse>> response =
201+
testRestTemplate.exchange(url, HttpMethod.GET, null, responseType);
202+
203+
// then
204+
assertAll(
205+
() -> assertTrue(response.getStatusCode().is2xxSuccessful()),
206+
() -> assertThat(response.getBody().data().rankings()).hasSize(2),
207+
() -> assertThat(response.getBody().data().rankings().get(0).productName()).isEqualTo("Jordan")
208+
);
209+
}
210+
}
211+
}

0 commit comments

Comments
 (0)