@@ -4,6 +4,17 @@ import { LeaderboardRepository } from '@/repositories/leaderboard.repository';
44
55jest . mock ( 'pg' ) ;
66
7+ // pg의 QueryResult 타입을 만족하는 mock 객체를 생성하기 위한 헬퍼 함수 생성
8+ function createMockQueryResult < T extends Record < string , unknown > > ( rows : T [ ] ) : QueryResult < T > {
9+ return {
10+ rows,
11+ rowCount : rows . length ,
12+ command : '' ,
13+ oid : 0 ,
14+ fields : [ ] ,
15+ } satisfies QueryResult < T > ;
16+ }
17+
718const mockPool : {
819 query : jest . Mock < Promise < QueryResult < Record < string , unknown > > > , unknown [ ] > ;
920} = {
@@ -18,7 +29,7 @@ describe('LeaderboardRepository', () => {
1829 } ) ;
1930
2031 describe ( 'getUserLeaderboard' , ( ) => {
21- it ( '사용자 리더보드를 조회할 수 있어야 한다' , async ( ) => {
32+ it ( '사용자 통계 배열로 이루어진 리더보드를 반환해야 한다' , async ( ) => {
2233 const mockResult = [
2334 {
2435 id : 1 ,
@@ -41,142 +52,71 @@ describe('LeaderboardRepository', () => {
4152 post_diff : 1 ,
4253 } ,
4354 ] ;
44-
45- mockPool . query . mockResolvedValue ( {
46- rows : mockResult ,
47- rowCount : mockResult . length ,
48- } as unknown as QueryResult ) ;
55+ mockPool . query . mockResolvedValue ( createMockQueryResult ( mockResult ) ) ;
4956
5057 const result = await repo . getUserLeaderboard ( 'viewCount' , 30 , 10 ) ;
5158
5259 expect ( mockPool . query ) . toHaveBeenCalledWith ( expect . stringContaining ( 'FROM users_user u' ) , expect . anything ( ) ) ;
5360 expect ( result ) . toEqual ( mockResult ) ;
5461 } ) ;
5562
56- it ( 'sort가 조회수인 경우 정렬 순서를 보장해야 한다.' , async ( ) => {
57- const mockResult = [
58- { view_diff : 20 , like_diff : 5 , post_diff : 1 } ,
59- { view_diff : 10 , like_diff : 10 , post_diff : 2 } ,
60- ] ;
61-
62- mockPool . query . mockResolvedValue ( {
63- rows : mockResult ,
64- rowCount : mockResult . length ,
65- } as unknown as QueryResult ) ;
66-
67- const result = await repo . getUserLeaderboard ( 'viewCount' , 30 , 10 ) ;
63+ it ( 'sort가 viewCount인 경우 view_diff 필드를 기준으로 내림차순 정렬해야 한다' , async ( ) => {
64+ await repo . getUserLeaderboard ( 'viewCount' , 30 , 10 ) ;
6865
69- expect ( result ) . toEqual ( mockResult ) ;
70- expect ( result [ 0 ] . view_diff ) . toBeGreaterThan ( result [ 1 ] . view_diff ) ;
66+ expect ( mockPool . query ) . toHaveBeenCalledWith (
67+ expect . stringContaining ( 'ORDER BY view_diff DESC' ) ,
68+ expect . anything ( ) ,
69+ ) ;
7170 } ) ;
7271
73- it ( 'sort가 좋아요 수인 경우 정렬 순서를 보장해야 한다.' , async ( ) => {
74- const mockResult = [
75- { view_diff : 10 , like_diff : 10 , post_diff : 1 } ,
76- { view_diff : 20 , like_diff : 5 , post_diff : 1 } ,
77- ] ;
78-
79- mockPool . query . mockResolvedValue ( {
80- rows : mockResult ,
81- rowCount : mockResult . length ,
82- } as unknown as QueryResult ) ;
72+ it ( 'sort가 likeCount인 경우 like_diff 필드를 기준으로 내림차순 정렬해야 한다' , async ( ) => {
73+ await repo . getUserLeaderboard ( 'likeCount' , 30 , 10 ) ;
8374
84- const result = await repo . getUserLeaderboard ( 'likeCount' , 30 , 10 ) ;
85-
86- expect ( result ) . toEqual ( mockResult ) ;
87- expect ( result [ 0 ] . like_diff ) . toBeGreaterThan ( result [ 1 ] . like_diff ) ;
75+ expect ( mockPool . query ) . toHaveBeenCalledWith (
76+ expect . stringContaining ( 'ORDER BY like_diff DESC' ) ,
77+ expect . anything ( ) ,
78+ ) ;
8879 } ) ;
8980
90- it ( 'sort가 게시물 수인 경우 정렬 순서를 보장해야 한다.' , async ( ) => {
91- const mockResult = [
92- { view_diff : 10 , like_diff : 10 , post_diff : 4 } ,
93- { view_diff : 20 , like_diff : 5 , post_diff : 1 } ,
94- ] ;
95-
96- mockPool . query . mockResolvedValue ( {
97- rows : mockResult ,
98- rowCount : mockResult . length ,
99- } as unknown as QueryResult ) ;
81+ it ( 'sort가 postCount인 경우 post_diff 필드를 기준으로 내림차순 정렬해야 한다' , async ( ) => {
82+ await repo . getUserLeaderboard ( 'postCount' , 30 , 10 ) ;
10083
101- const result = await repo . getUserLeaderboard ( 'postCount' , 30 , 10 ) ;
102-
103- expect ( result ) . toEqual ( mockResult ) ;
104- expect ( result [ 0 ] . post_diff ) . toBeGreaterThan ( result [ 1 ] . post_diff ) ;
84+ expect ( mockPool . query ) . toHaveBeenCalledWith (
85+ expect . stringContaining ( 'ORDER BY post_diff DESC' ) ,
86+ expect . anything ( ) ,
87+ ) ;
10588 } ) ;
10689
107- it ( 'limit 만큼의 데이터만 반환해야 한다' , async ( ) => {
108- const mockData = [
109- { id : 1 , title : 'test' } ,
110- { id : 2 , title : 'test2' } ,
111- { id : 3 , title : 'test3' } ,
112- { id : 4 , title : 'test4' } ,
113- { id : 5 , title : 'test5' } ,
114- ] ;
90+ it ( 'limit 파라미터가 쿼리에 올바르게 적용되어야 한다' , async ( ) => {
11591 const mockLimit = 5 ;
11692
117- mockPool . query . mockResolvedValue ( {
118- rows : mockData ,
119- rowCount : mockData . length ,
120- } as unknown as QueryResult ) ;
121-
122- const result = await repo . getUserLeaderboard ( 'viewCount' , 30 , mockLimit ) ;
123-
124- expect ( result ) . toEqual ( mockData ) ;
125- expect ( result . length ) . toEqual ( mockLimit ) ;
93+ await repo . getUserLeaderboard ( 'viewCount' , 30 , mockLimit ) ;
12694
12795 expect ( mockPool . query ) . toHaveBeenCalledWith (
12896 expect . stringContaining ( 'LIMIT $2' ) ,
12997 expect . arrayContaining ( [ 30 , mockLimit ] ) ,
13098 ) ;
13199 } ) ;
132100
133- it ( 'GROUP BY 절이 포함되어야 한다' , async ( ) => {
134- mockPool . query . mockResolvedValue ( {
135- rows : [ ] ,
136- rowCount : 0 ,
137- } as unknown as QueryResult ) ;
138-
139- await repo . getUserLeaderboard ( 'viewCount' , 30 , 10 ) ;
140-
141- expect ( mockPool . query ) . toHaveBeenCalledWith ( expect . stringContaining ( 'GROUP BY u.id, u.email' ) , expect . anything ( ) ) ;
142- } ) ;
143-
144101 it ( 'dateRange 파라미터가 쿼리에 올바르게 적용되어야 한다' , async ( ) => {
145- const mockResult = [ { id : 1 } ] ;
146- const testDateRange = 30 ;
147-
148- mockPool . query . mockResolvedValue ( {
149- rows : mockResult ,
150- rowCount : mockResult . length ,
151- } as unknown as QueryResult ) ;
102+ const mockDateRange = 30 ;
152103
153- await repo . getUserLeaderboard ( 'viewCount' , testDateRange , 10 ) ;
104+ await repo . getUserLeaderboard ( 'viewCount' , mockDateRange , 10 ) ;
154105
155106 expect ( mockPool . query ) . toHaveBeenCalledWith (
156- expect . stringContaining ( '$1::int' ) ,
157- expect . arrayContaining ( [ testDateRange , expect . anything ( ) ] ) ,
107+ expect . stringContaining ( 'make_interval(days := $1::int) ' ) ,
108+ expect . arrayContaining ( [ mockDateRange , expect . anything ( ) ] ) ,
158109 ) ;
159110 } ) ;
160111
161- it ( '데이터가 없는 경우 빈 배열을 반환해야 한다' , async ( ) => {
162- mockPool . query . mockResolvedValue ( {
163- rows : [ ] ,
164- rowCount : 0 ,
165- } as unknown as QueryResult ) ;
166-
167- const result = await repo . getUserLeaderboard ( 'viewCount' , 30 , 10 ) ;
168-
169- expect ( result ) . toEqual ( [ ] ) ;
170- } ) ;
171-
172112 it ( '에러 발생 시 DBError를 던져야 한다' , async ( ) => {
173113 mockPool . query . mockRejectedValue ( new Error ( 'DB connection failed' ) ) ;
174114 await expect ( repo . getUserLeaderboard ( 'viewCount' , 30 , 10 ) ) . rejects . toThrow ( DBError ) ;
175115 } ) ;
176116 } ) ;
177117
178118 describe ( 'getPostLeaderboard' , ( ) => {
179- it ( '게시물 리더보드를 조회할 수 있어야 한다' , async ( ) => {
119+ it ( '게시물 통계 배열로 이루어진 리더보드를 반환해야 한다' , async ( ) => {
180120 const mockResult = [
181121 {
182122 id : 2 ,
@@ -200,54 +140,52 @@ describe('LeaderboardRepository', () => {
200140 } ,
201141 ] ;
202142
203- mockPool . query . mockResolvedValue ( {
204- rows : mockResult ,
205- rowCount : mockResult . length ,
206- } as unknown as QueryResult ) ;
143+ mockPool . query . mockResolvedValue ( createMockQueryResult ( mockResult ) ) ;
207144
208145 const result = await repo . getPostLeaderboard ( 'viewCount' , 30 , 10 ) ;
209146
210147 expect ( result ) . toEqual ( mockResult ) ;
211148 expect ( mockPool . query ) . toHaveBeenCalledWith ( expect . stringContaining ( 'FROM posts_post p' ) , expect . anything ( ) ) ;
212149 } ) ;
213150
214- it ( 'GROUP BY 절이 포함되지 않아야 한다' , async ( ) => {
215- mockPool . query . mockResolvedValue ( {
216- rows : [ ] ,
217- rowCount : 0 ,
218- } as unknown as QueryResult ) ;
219-
151+ it ( 'sort가 viewCount인 경우 view_diff 필드를 기준으로 내림차순 정렬해야 한다' , async ( ) => {
220152 await repo . getPostLeaderboard ( 'viewCount' , 30 , 10 ) ;
221153
222- expect ( mockPool . query ) . toHaveBeenCalledWith ( expect . not . stringContaining ( 'GROUP BY' ) , expect . anything ( ) ) ;
154+ expect ( mockPool . query ) . toHaveBeenCalledWith (
155+ expect . stringContaining ( 'ORDER BY view_diff DESC' ) ,
156+ expect . anything ( ) ,
157+ ) ;
223158 } ) ;
224159
225- it ( 'dateRange 파라미터가 쿼리에 올바르게 적용되어야 한다' , async ( ) => {
226- const mockResult = [ { id : 1 } ] ;
227- const testDateRange = 30 ;
160+ it ( 'sort가 likeCount인 경우 like_diff 필드를 기준으로 내림차순 정렬해야 한다' , async ( ) => {
161+ await repo . getPostLeaderboard ( 'likeCount' , 30 , 10 ) ;
228162
229- mockPool . query . mockResolvedValue ( {
230- rows : mockResult ,
231- rowCount : mockResult . length ,
232- } as unknown as QueryResult ) ;
163+ expect ( mockPool . query ) . toHaveBeenCalledWith (
164+ expect . stringContaining ( 'ORDER BY like_diff DESC' ) ,
165+ expect . anything ( ) ,
166+ ) ;
167+ } ) ;
168+
169+ it ( 'limit 파라미터가 쿼리에 올바르게 적용되어야 한다' , async ( ) => {
170+ const mockLimit = 5 ;
233171
234- await repo . getPostLeaderboard ( 'viewCount' , testDateRange , 10 ) ;
172+ await repo . getPostLeaderboard ( 'viewCount' , 30 , mockLimit ) ;
235173
236174 expect ( mockPool . query ) . toHaveBeenCalledWith (
237- expect . stringContaining ( '$1::int ' ) ,
238- expect . arrayContaining ( [ testDateRange , expect . anything ( ) ] ) ,
175+ expect . stringContaining ( 'LIMIT $2 ' ) ,
176+ expect . arrayContaining ( [ 30 , mockLimit ] ) ,
239177 ) ;
240178 } ) ;
241179
242- it ( '데이터가 없는 경우 빈 배열을 반환해야 한다' , async ( ) => {
243- mockPool . query . mockResolvedValue ( {
244- rows : [ ] ,
245- rowCount : 0 ,
246- } as unknown as QueryResult ) ;
180+ it ( 'dateRange 파라미터가 쿼리에 올바르게 적용되어야 한다' , async ( ) => {
181+ const mockDateRange = 30 ;
247182
248- const result = await repo . getPostLeaderboard ( 'viewCount' , 30 , 10 ) ;
183+ await repo . getPostLeaderboard ( 'viewCount' , mockDateRange , 10 ) ;
249184
250- expect ( result ) . toEqual ( [ ] ) ;
185+ expect ( mockPool . query ) . toHaveBeenCalledWith (
186+ expect . stringContaining ( 'make_interval(days := $1::int)' ) ,
187+ expect . arrayContaining ( [ mockDateRange , expect . anything ( ) ] ) ,
188+ ) ;
251189 } ) ;
252190
253191 it ( '에러 발생 시 DBError를 던져야 한다' , async ( ) => {
0 commit comments