Repository navigation
Expand file tree
/
Copy pathBeachControllerIntegrationTest.java
More file actions
172 lines (153 loc) · 7.46 KB
/
Copy pathBeachControllerIntegrationTest.java
File metadata and controls
172 lines (153 loc) · 7.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
package com.beachcheck.beach.integration;
import static com.beachcheck.support.fixture.BeachTestFixtures.createBeachWithLocation;
import static com.beachcheck.support.fixture.UniqueTestFixtures.uniqueBeachCode;
import static com.beachcheck.support.fixture.UniqueTestFixtures.uniqueCode;
import static com.beachcheck.support.fixture.UniqueTestFixtures.uniqueEmail;
import static com.beachcheck.support.fixture.UserFavoriteTestFixtures.createFavorite;
import static com.beachcheck.support.fixture.UserTestFixtures.createUser;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.hasItem;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import com.beachcheck.beach.domain.Beach;
import com.beachcheck.beach.repository.BeachRepository;
import com.beachcheck.support.base.ApiTest;
import com.beachcheck.support.fixture.ApiErrorTestFixtures;
import com.beachcheck.support.fixture.ApiRoutes;
import com.beachcheck.user.domain.User;
import com.beachcheck.user.repository.UserFavoriteRepository;
import com.beachcheck.user.repository.UserRepository;
import com.fasterxml.jackson.databind.JsonNode;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.test.web.servlet.MvcResult;
@DisplayName("BeachController 통합 테스트")
class BeachControllerIntegrationTest extends ApiTest {
@Autowired private BeachRepository beachRepository;
@Autowired private UserRepository userRepository;
@Autowired private UserFavoriteRepository userFavoriteRepository;
@Autowired private CacheManager cacheManager;
private User user;
private Beach favoriteBeach;
private Beach otherBeach;
private Beach nearbyBeach;
private Beach farBeach;
private String searchCodePrefix;
private String searchTag;
private Cache getBeachSummariesCache() {
return java.util.Objects.requireNonNull(
cacheManager.getCache("beachSummaries"), "beachSummaries 캐시가 설정되지 않았습니다. 캐시 설정을 확인하세요.");
}
@BeforeEach
void setUp() {
getBeachSummariesCache().clear();
user =
userRepository.save(createUser(uniqueEmail("beach-controller"), "Beach Controller User"));
searchCodePrefix = uniqueCode("BEACH_API");
searchTag = uniqueCode("tag");
favoriteBeach =
saveBeach(searchCodePrefix + "_A", "Favorite Beach", 129.1603, 35.1587, searchTag, "busy");
otherBeach =
saveBeach(searchCodePrefix + "_B", "Other Beach", 129.1608, 35.1592, "other-tag", "normal");
nearbyBeach =
saveBeach(uniqueBeachCode(), "Nearby Beach", 10.0010, 10.0010, "nearby", "normal");
farBeach = saveBeach(uniqueBeachCode(), "Far Beach", 30.0000, 30.0000, "far", "normal");
userFavoriteRepository.save(createFavorite(user, favoriteBeach));
}
@Test
@DisplayName("TC1: 기본 목록 조회는 anonymous 요청에도 응답한다")
void searchBeaches_defaultList_anonymous_returnsBeaches() throws Exception {
mockMvc
.perform(get(ApiRoutes.BEACHES))
.andExpect(status().isOk())
.andExpect(
jsonPath(
"$[?(@.id=='" + favoriteBeach.getId() + "')].id",
hasItem(favoriteBeach.getId().toString())))
.andExpect(
jsonPath("$[?(@.id=='" + favoriteBeach.getId() + "')].isFavorite", hasItem(false)));
}
@Test
@DisplayName("TC2: 기본 목록 조회는 인증 사용자의 찜 여부를 반영한다")
void searchBeaches_defaultList_authenticated_reflectsFavorite() throws Exception {
mockMvc
.perform(get(ApiRoutes.BEACHES).header("Authorization", authHeader(user)))
.andExpect(status().isOk())
.andExpect(
jsonPath("$[?(@.id=='" + favoriteBeach.getId() + "')].isFavorite", hasItem(true)))
.andExpect(jsonPath("$[?(@.id=='" + otherBeach.getId() + "')].isFavorite", hasItem(false)));
}
@Test
@DisplayName("TC3: 검색 요청은 q/tag 분기로 필터링되고 인증 사용자의 찜 여부를 반영한다")
void searchBeaches_searchBranch_authenticated_reflectsFavorite() throws Exception {
mockMvc
.perform(
get(ApiRoutes.BEACHES)
.param("q", searchCodePrefix)
.param("tag", searchTag)
.header("Authorization", authHeader(user)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.length()").value(1))
.andExpect(jsonPath("$[0].id").value(favoriteBeach.getId().toString()))
.andExpect(jsonPath("$[0].code").value(favoriteBeach.getCode()))
.andExpect(jsonPath("$[0].tag").value(searchTag))
.andExpect(jsonPath("$[0].isFavorite").value(true));
}
@Test
@DisplayName("TC4: 반경 검색은 nearby 분기로 라우팅되어 반경 내 해변만 반환한다")
void searchBeaches_radiusBranch_returnsNearbyBeaches() throws Exception {
mockMvc
.perform(
get(ApiRoutes.BEACHES).param("lat", "10.0").param("lon", "10.0").param("radiusKm", "5"))
.andExpect(status().isOk())
.andExpect(
jsonPath(
"$[?(@.id=='" + nearbyBeach.getId() + "')].id",
hasItem(nearbyBeach.getId().toString())))
.andExpect(jsonPath("$[?(@.id=='" + farBeach.getId() + "')]").isEmpty());
}
@Test
@DisplayName("TC5: 반경 검색 파라미터가 일부만 오면 400 ProblemDetail을 반환한다")
void searchBeaches_partialRadiusParams_returnsBadRequestProblemDetail() throws Exception {
mockMvc
.perform(get(ApiRoutes.BEACHES).param("lat", "35.1587").param("lon", "129.1603"))
.andExpect(status().isBadRequest())
.andExpect(ApiErrorTestFixtures.problemDetailStatus(objectMapper, 400))
.andExpect(jsonPath("$.title").value("Invalid Request"))
.andExpect(
jsonPath("$.detail")
.value("Radius search requires all three parameters: lat, lon, radiusKm"));
}
@Test
@DisplayName("TC6: 위도 검증 실패는 400 Validation Error 계약으로 고정된다")
void searchBeaches_invalidLatitude_returnsBadRequest() throws Exception {
MvcResult result =
mockMvc
.perform(
get(ApiRoutes.BEACHES)
.param("lat", "91")
.param("lon", "129.1603")
.param("radiusKm", "1"))
.andExpect(status().isBadRequest())
.andExpect(ApiErrorTestFixtures.problemDetailStatus(objectMapper, 400))
.andReturn();
JsonNode problem = objectMapper.readTree(result.getResponse().getContentAsString());
assertThat(problem.path("type").asText()).isEqualTo("about:blank");
assertThat(problem.path("title").asText()).isEqualTo("Bad Request");
assertThat(problem.path("status").asInt()).isEqualTo(400);
assertThat(problem.path("instance").asText()).isEqualTo(ApiRoutes.BEACHES);
assertThat(problem.path("detail").asText()).isNotBlank();
}
private Beach saveBeach(
String code, String name, double lon, double lat, String tag, String status) {
Beach beach = createBeachWithLocation(code, name, lon, lat);
beach.setTag(tag);
beach.setStatus(status);
return beachRepository.save(beach);
}
}