88import java .util .List ;
99import java .util .Optional ;
1010import java .util .Set ;
11- import java .util .stream .Collectors ;
1211
1312import org .junit .jupiter .api .DisplayName ;
1413import org .junit .jupiter .api .Test ;
2221import com .example .log4u .domain .diary .service .DiaryGeohashService ;
2322import com .example .log4u .domain .diary .service .DiaryService ;
2423import com .example .log4u .domain .map .cache .dao .ClusterCacheDao ;
25- import com .example .log4u .domain .map .cache .dao .DiaryCacheDao ;
24+ import com .example .log4u .domain .map .cache .dao .MarkerCacheDao ;
2625import com .example .log4u .domain .map .dto .response .DiaryClusterResponseDto ;
2726import com .example .log4u .domain .map .dto .response .DiaryMarkerResponseDto ;
2827import com .example .log4u .domain .map .exception .InvalidGeohashException ;
@@ -50,7 +49,7 @@ class MapServiceTest {
5049 private DiaryRepository diaryRepository ;
5150
5251 @ Mock
53- private DiaryCacheDao diaryCacheDao ;
52+ private MarkerCacheDao markerCacheDao ;
5453
5554 @ Mock
5655 private DiaryService diaryService ;
@@ -61,222 +60,106 @@ class MapServiceTest {
6160 @ Mock
6261 private ClusterCacheDao clusterCacheDao ;
6362
64- private static final String GEOHASH = "abc" ;
65- private static final int VALID_LEVEL_1 = 1 ;
66- private static final int VALID_LEVEL_2 = 2 ;
63+ private static final String GEOHASH_L3 = "abc" ;
64+ private static final String GEOHASH_L5 = "abcde" ;
6765
68- private final List <DiaryClusterResponseDto > mockResult = List .of (
66+ private static final int LEVEL_SIDO = 1 ;
67+ private static final int LEVEL_SIGG = 2 ;
68+
69+ private final List <DiaryClusterResponseDto > clusters = List .of (
6970 new DiaryClusterResponseDto ("서울" , 1L , 37.5665 , 126.9780 , 10L )
7071 );
7172
73+ private final List <DiaryMarkerResponseDto > markers = List .of (
74+ DiaryMarkerResponseDto .of (
75+ DiaryFixture .createDiaryFixture (1L )
76+ )
77+ );
78+
7279 @ DisplayName ("성공: 클러스터 캐시 HIT" )
7380 @ Test
74- void getDiaryClusters_success_cacheHit () {
81+ void getDiaryClusters_cacheHit () {
7582 // given
76- given (clusterCacheDao .getDiaryCluster ( GEOHASH , VALID_LEVEL_1 )).willReturn (Optional . of ( mockResult ) );
83+ given (clusterCacheDao .load ( GEOHASH_L3 , LEVEL_SIDO )).willReturn (clusters );
7784
7885 // when
79- List <DiaryClusterResponseDto > result = mapService .getDiaryClusters (GEOHASH , VALID_LEVEL_1 );
86+ List <DiaryClusterResponseDto > result = mapService .getDiaryClusters (GEOHASH_L3 , LEVEL_SIDO );
8087
8188 // then
82- assertThat (result ).isEqualTo (mockResult );
83- verify (sidoAreasRepository , never ()). findByGeohashPrefix ( any () );
84- verify (clusterCacheDao , never ()).setDiaryCluster ( any (), anyInt (), any (), any ());
89+ assertThat (result ).isEqualTo (clusters );
90+ verify (clusterCacheDao ). load ( GEOHASH_L3 , LEVEL_SIDO );
91+ verify (clusterCacheDao , never ()).loadAndCache ( anyString (), anyInt ());
8592 }
8693
87- @ DisplayName ("성공: 캐시 MISS → DB 조회 → 캐시 저장" )
94+
95+ @ DisplayName ("성공: 캐시 MISS → DAO.loadAndCache 호출" )
8896 @ Test
89- void getDiaryClusters_success_cacheMiss_then_dbHit_and_cacheStore () {
97+ void getDiaryClusters_cacheMiss_thenLoadAndCache () {
9098 // given
91- given (clusterCacheDao .getDiaryCluster ( GEOHASH , VALID_LEVEL_1 )).willReturn (Optional . empty () );
92- given (sidoAreasRepository . findByGeohashPrefix ( GEOHASH )).willReturn (mockResult );
99+ given (clusterCacheDao .load ( GEOHASH_L3 , LEVEL_SIDO )).willReturn (null );
100+ given (clusterCacheDao . loadAndCache ( GEOHASH_L3 , LEVEL_SIDO )).willReturn (clusters );
93101
94102 // when
95- List <DiaryClusterResponseDto > result = mapService .getDiaryClusters (GEOHASH , VALID_LEVEL_1 );
103+ List <DiaryClusterResponseDto > result = mapService .getDiaryClusters (GEOHASH_L3 , LEVEL_SIDO );
96104
97105 // then
98- assertThat (result ).isEqualTo (mockResult );
99- verify (clusterCacheDao ).setDiaryCluster (eq (GEOHASH ), eq (VALID_LEVEL_1 ), eq (mockResult ), any ());
100- }
101-
102- @ DisplayName ("실패: 유효하지 않은 level" )
103- @ Test
104- void getDiaryClusters_invalidLevel () {
105- // given
106- int invalidLevel = 99 ;
107-
108- // expect
109- assertThatThrownBy (() -> mapService .getDiaryClusters (GEOHASH , invalidLevel ))
110- .isInstanceOf (InvalidMapLevelException .class );
106+ assertThat (result ).isEqualTo (clusters );
107+ verify (clusterCacheDao ).load (GEOHASH_L3 , LEVEL_SIDO );
108+ verify (clusterCacheDao ).loadAndCache (GEOHASH_L3 , LEVEL_SIDO );
111109 }
112110
113- @ DisplayName ("실패: geohash 길이 불일치" )
111+ @ DisplayName ("실패: geohash 길이 불일치(클러스터) " )
114112 @ Test
115113 void getDiaryClusters_invalidGeohashLength () {
116114 // given
117- String invalidGeohash = "abcd" ;
115+ String invalid = "abcd" ; // 길이 4 → level(1/2) 기대 길이 3과 불일치
118116
119117 // expect
120- assertThatThrownBy (() -> mapService .getDiaryClusters (invalidGeohash , VALID_LEVEL_1 ))
121- .isInstanceOf (InvalidGeohashException .class )
122- .hasMessageContaining ("geohash 길이가 유효하지 않습니다" );
123- }
124-
125- @ DisplayName ("성공 : geohash 캐시 HIT + 모든 diary 캐시 HIT" )
126- @ Test
127- void getDiariesByGeohash_success_allCacheHit () {
128- // given
129- String geohash = "wydmt" ;
130- Set <Long > cachedIds = Set .of (1L , 2L );
131- DiaryMarkerResponseDto dto1 = DiaryMarkerFixture .createDiaryMarker (1L );
132- DiaryMarkerResponseDto dto2 = DiaryMarkerFixture .createDiaryMarker (2L );
133-
134- given (diaryCacheDao .getDiaryIdSetFromCache ("wydmt" )).willReturn (cachedIds );
135- given (diaryCacheDao .getDiariesFromCacheBulk (anyList ())).willReturn (List .of (dto1 , dto2 ));
136-
137- // when
138- List <DiaryMarkerResponseDto > result = mapService .getDiariesByGeohash ("wydmt" );
139-
140- // then
141- assertThat (result ).containsExactlyInAnyOrder (dto1 , dto2 );
142- verify (diaryRepository , never ()).findAllById (any ());
143- verify (diaryGeohashService , never ()).getDiaryIdsByGeohash (any ());
144- }
145-
146- @ DisplayName ("성공 : geohash 캐시 HIT + 일부 diary 캐시 MISS" )
147- @ Test
148- void getDiariesByGeohash_success_partialDiaryCacheMiss () {
149- // given
150- String geohash = "wydmt" ;
151- Set <Long > cachedIds = Set .of (1L , 2L );
152- List <Long > sortedIds = new ArrayList <>(cachedIds );
153- Collections .sort (sortedIds );
154-
155- DiaryMarkerResponseDto dto1 = DiaryMarkerFixture .createDiaryMarker (1L );
156- Diary diary2 = DiaryFixture .createDiaryFixture (2L );
157- DiaryMarkerResponseDto dto2 = DiaryMarkerResponseDto .of (diary2 );
158-
159- given (diaryCacheDao .getDiaryIdSetFromCache ("wydmt" )).willReturn (cachedIds );
160- given (diaryCacheDao .getDiariesFromCacheBulk (anyList ()))
161- .willReturn (List .of (dto1 ));
162- given (diaryService .getDiaries (List .of (2L ))).willReturn (List .of (diary2 ));
163-
164- // when
165- List <DiaryMarkerResponseDto > result = mapService .getDiariesByGeohash ("wydmt" );
166-
167- // then
168- assertThat (result ).containsExactlyInAnyOrder (dto1 , dto2 );
169- verify (diaryCacheDao ).cacheAllDiaries (List .of (dto2 ));
170- }
171-
172-
173- @ DisplayName ("성공 : geohash 캐시 MISS → DB 조회 → 모든 diary 캐시 HIT" )
174- @ Test
175- void getDiariesByGeohash_success_geohashMiss_allDiaryCacheHit () {
176- // given
177- String geohash = "wydmt" ;
178- List <Long > diaryIdsFromDb = List .of (1L , 2L );
179- DiaryMarkerResponseDto dto1 = DiaryMarkerFixture .createDiaryMarker (1L );
180- DiaryMarkerResponseDto dto2 = DiaryMarkerFixture .createDiaryMarker (2L );
181-
182- given (diaryCacheDao .getDiaryIdSetFromCache (geohash )).willReturn (Collections .emptySet ());
183- given (diaryGeohashService .getDiaryIdsByGeohash (geohash )).willReturn (diaryIdsFromDb );
184- given (diaryCacheDao .getDiariesFromCacheBulk (diaryIdsFromDb )).willReturn (List .of (dto1 , dto2 ));
185-
186- // when
187- List <DiaryMarkerResponseDto > result = mapService .getDiariesByGeohash (geohash );
188-
189- // then
190- assertThat (result ).containsExactlyInAnyOrder (dto1 , dto2 );
191- verify (diaryCacheDao ).cacheDiaryIdSetByGeohash (geohash , diaryIdsFromDb );
192- verify (diaryService , never ()).getDiaries (any ());
118+ assertThatThrownBy (() -> mapService .getDiaryClusters (invalid , LEVEL_SIDO ))
119+ .isInstanceOf (InvalidGeohashException .class );
120+ verifyNoInteractions (clusterCacheDao );
193121 }
194122
195- @ DisplayName ("성공 : geohash 캐시 MISS → DB 조회 → 모든 diary 캐시 MISS " )
123+ @ DisplayName ("성공: 마커 캐시 HIT " )
196124 @ Test
197- void getDiariesByGeohash_success_geohashMiss_allDiaryCacheMiss () {
125+ void getDiaryMarkers_cacheHit () {
198126 // given
199- String geohash = "wydmt" ;
200- List <Long > diaryIdsFromDb = List .of (1L , 2L );
201- Diary diary1 = DiaryFixture .createDiaryFixture (1L );
202- Diary diary2 = DiaryFixture .createDiaryFixture (2L );
203- DiaryMarkerResponseDto dto1 = DiaryMarkerResponseDto .of (diary1 );
204- DiaryMarkerResponseDto dto2 = DiaryMarkerResponseDto .of (diary2 );
205-
206- given (diaryCacheDao .getDiaryIdSetFromCache (geohash )).willReturn (Collections .emptySet ());
207- given (diaryGeohashService .getDiaryIdsByGeohash (geohash )).willReturn (diaryIdsFromDb );
208- given (diaryCacheDao .getDiariesFromCacheBulk (diaryIdsFromDb )).willReturn (List .of ());
209- given (diaryService .getDiaries (diaryIdsFromDb )).willReturn (List .of (diary1 , diary2 ));
127+ given (markerCacheDao .load (GEOHASH_L5 )).willReturn (markers );
210128
211129 // when
212- List <DiaryMarkerResponseDto > result = mapService .getDiariesByGeohash ( geohash );
130+ List <DiaryMarkerResponseDto > result = mapService .getDiaryMarkers ( GEOHASH_L5 );
213131
214132 // then
215- assertThat (result ).containsExactlyInAnyOrder ( dto1 , dto2 );
216- verify (diaryCacheDao ). cacheDiaryIdSetByGeohash ( geohash , diaryIdsFromDb );
217- verify (diaryCacheDao ). cacheAllDiaries ( List . of ( dto1 , dto2 ));
133+ assertThat (result ).isEqualTo ( markers );
134+ verify (markerCacheDao ). load ( GEOHASH_L5 );
135+ verify (markerCacheDao , never ()). loadAndCache ( anyString ( ));
218136 }
219137
220- @ DisplayName ("성공 : geohash 캐시 MISS → DB 조회 → diary 일부 캐시 MISS " )
138+ @ DisplayName ("성공: 마커 캐시 MISS → DAO.loadAndCache 호출 " )
221139 @ Test
222- void getDiariesByGeohash_success_geohashCacheMissAndDiaryCacheMiss () {
140+ void getDiaryMarkers_cacheMiss_thenLoadAndCache () {
223141 // given
224- String geohash = "abcde" ;
225- List <Long > dbIds = List .of (1L , 2L );
226- DiaryMarkerResponseDto dto1 = DiaryMarkerFixture .createDiaryMarker (1L );
227- Diary diary2 = DiaryFixture .createDiaryFixture (2L );
228- DiaryMarkerResponseDto dto2 = DiaryMarkerResponseDto .of (diary2 );
229-
230- given (diaryCacheDao .getDiaryIdSetFromCache (geohash )).willReturn (Collections .emptySet ());
231- given (diaryGeohashService .getDiaryIdsByGeohash (geohash )).willReturn (dbIds );
232- given (diaryCacheDao .getDiariesFromCacheBulk (dbIds )).willReturn (List .of (dto1 ));
233- given (diaryService .getDiaries (List .of (2L ))).willReturn (List .of (diary2 ));
142+ given (markerCacheDao .load (GEOHASH_L5 )).willReturn (null );
143+ given (markerCacheDao .loadAndCache (GEOHASH_L5 )).willReturn (markers );
234144
235145 // when
236- List <DiaryMarkerResponseDto > result = mapService .getDiariesByGeohash ( geohash );
146+ List <DiaryMarkerResponseDto > result = mapService .getDiaryMarkers ( GEOHASH_L5 );
237147
238148 // then
239- assertThat (result ).containsExactlyInAnyOrder ( dto1 , dto2 );
240- verify (diaryCacheDao ). cacheDiaryIdSetByGeohash ( geohash , dbIds );
241- verify (diaryCacheDao ). cacheAllDiaries ( List . of ( dto2 ) );
149+ assertThat (result ).isEqualTo ( markers );
150+ verify (markerCacheDao ). load ( GEOHASH_L5 );
151+ verify (markerCacheDao ). loadAndCache ( GEOHASH_L5 );
242152 }
243153
244- @ DisplayName ("성공 : Redis 예외 발생 시 fallback 작동: DB에서 조회됨 " )
154+ @ DisplayName ("실패: geohash 길이 불일치(마커) " )
245155 @ Test
246- void getDiariesByGeohash_redisFailureHandledInternally () {
156+ void getDiaryMarkers_invalidGeohashLength () {
247157 // given
248- String geohash = "abcde" ;
249- given (diaryCacheDao .getDiaryIdSetFromCache (geohash )).willReturn (Collections .emptySet ());
250-
251- List <Long > diaryIds = List .of (1L );
252- Diary diary = DiaryFixture .createDiaryFixture (1L );
253- given (diaryGeohashService .getDiaryIdsByGeohash (geohash )).willReturn (diaryIds );
254- given (diaryService .getDiaries (diaryIds )).willReturn (List .of (diary ));
158+ String invalid = "abcd" ;
255159
256- // when
257- List <DiaryMarkerResponseDto > result = mapService .getDiariesByGeohash (geohash );
258-
259- // then
260- assertThat (result ).hasSize (1 );
261- assertThat (result .getFirst ().diaryId ()).isEqualTo (1L );
262- verify (diaryService ).getDiaries (diaryIds );
263- }
264-
265- @ DisplayName ("예외 : diaryId 존재하나 DB에 diary 없음" )
266- @ Test
267- void diaryIdExistsButDiaryMissingInDb () {
268- // given
269- String geohash = "wydmt" ;
270- Set <Long > cachedIds = Set .of (100L );
271- given (diaryCacheDao .getDiaryIdSetFromCache (geohash )).willReturn (cachedIds );
272- given (diaryCacheDao .getDiariesFromCacheBulk (List .of (100L ))).willReturn (List .of ());
273- given (diaryService .getDiaries (List .of (100L ))).willReturn (Collections .emptyList ());
274-
275- // when
276- List <DiaryMarkerResponseDto > result = mapService .getDiariesByGeohash (geohash );
277-
278- // then
279- assertThat (result ).isEmpty ();
160+ // expect
161+ assertThatThrownBy (() -> mapService .getDiaryMarkers (invalid ))
162+ .isInstanceOf (InvalidGeohashException .class );
163+ verifyNoInteractions (markerCacheDao );
280164 }
281-
282165}
0 commit comments