@@ -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