|
1 | 1 | import { Pool } from 'pg'; |
2 | | -import { DBError } from '@/exception'; |
| 2 | +import { DBError, NotFoundError } from '@/exception'; |
3 | 3 | import { UserLeaderboardSortType, PostLeaderboardSortType } from '@/types'; |
4 | 4 | import { LeaderboardRepository } from '@/repositories/leaderboard.repository'; |
5 | 5 | import { mockPool, createMockQueryResult } from '@/utils/fixtures'; |
@@ -184,4 +184,118 @@ describe('LeaderboardRepository', () => { |
184 | 184 | await expect(repo.getPostLeaderboard('viewCount', 30, 10)).rejects.toThrow(DBError); |
185 | 185 | }); |
186 | 186 | }); |
| 187 | + |
| 188 | + describe('getUserStats', () => { |
| 189 | + const mockUserStats = { |
| 190 | + username: 'test-user', |
| 191 | + total_views: '1000', |
| 192 | + total_likes: '50', |
| 193 | + total_posts: '10', |
| 194 | + view_diff: '100', |
| 195 | + like_diff: '5', |
| 196 | + post_diff: '2', |
| 197 | + }; |
| 198 | + |
| 199 | + it('특정 사용자의 통계를 반환해야 한다', async () => { |
| 200 | + mockPool.query.mockResolvedValue(createMockQueryResult([mockUserStats])); |
| 201 | + |
| 202 | + const result = await repo.getUserStats('test-user', 30); |
| 203 | + |
| 204 | + expect(mockPool.query).toHaveBeenCalledWith(expect.stringContaining('WHERE u.username = $1'), ['test-user']); |
| 205 | + expect(result).toEqual(mockUserStats); |
| 206 | + }); |
| 207 | + |
| 208 | + it('username 파라미터가 쿼리에 올바르게 적용되어야 한다', async () => { |
| 209 | + mockPool.query.mockResolvedValue(createMockQueryResult([mockUserStats])); |
| 210 | + |
| 211 | + await repo.getUserStats('test-user', 30); |
| 212 | + |
| 213 | + expect(mockPool.query).toHaveBeenCalledWith(expect.stringContaining('GROUP BY u.username'), ['test-user']); |
| 214 | + }); |
| 215 | + |
| 216 | + it('사용자가 존재하지 않으면 NotFoundError를 던져야 한다', async () => { |
| 217 | + mockPool.query.mockResolvedValue(createMockQueryResult([])); |
| 218 | + |
| 219 | + await expect(repo.getUserStats('non-existent', 30)).rejects.toThrow(NotFoundError); |
| 220 | + await expect(repo.getUserStats('non-existent', 30)).rejects.toThrow('사용자를 찾을 수 없습니다: non-existent'); |
| 221 | + }); |
| 222 | + |
| 223 | + it('CTE 쿼리가 포함되어야 한다', async () => { |
| 224 | + mockPool.query.mockResolvedValue(createMockQueryResult([mockUserStats])); |
| 225 | + |
| 226 | + await repo.getUserStats('test-user', 30); |
| 227 | + |
| 228 | + expect(mockPool.query).toHaveBeenCalledWith(expect.stringContaining('WITH'), expect.anything()); |
| 229 | + expect(mockPool.query).toHaveBeenCalledWith(expect.stringContaining('today_stats'), expect.anything()); |
| 230 | + }); |
| 231 | + |
| 232 | + it('쿼리 에러 발생 시 DBError를 던져야 한다', async () => { |
| 233 | + mockPool.query.mockRejectedValue(new Error('DB connection failed')); |
| 234 | + |
| 235 | + await expect(repo.getUserStats('test-user', 30)).rejects.toThrow(DBError); |
| 236 | + await expect(repo.getUserStats('test-user', 30)).rejects.toThrow('사용자 통계 조회 중 문제가 발생했습니다.'); |
| 237 | + }); |
| 238 | + }); |
| 239 | + |
| 240 | + describe('getRecentPosts', () => { |
| 241 | + const mockRecentPosts = [ |
| 242 | + { |
| 243 | + title: 'Test Post 1', |
| 244 | + released_at: '2025-01-01', |
| 245 | + today_view: '100', |
| 246 | + today_like: '10', |
| 247 | + view_diff: '20', |
| 248 | + }, |
| 249 | + { |
| 250 | + title: 'Test Post 2', |
| 251 | + released_at: '2025-01-02', |
| 252 | + today_view: '200', |
| 253 | + today_like: '20', |
| 254 | + view_diff: '30', |
| 255 | + }, |
| 256 | + ]; |
| 257 | + |
| 258 | + it('특정 사용자의 최근 게시글을 반환해야 한다', async () => { |
| 259 | + mockPool.query.mockResolvedValue(createMockQueryResult(mockRecentPosts)); |
| 260 | + |
| 261 | + const result = await repo.getRecentPosts('test-user', 30, 3); |
| 262 | + |
| 263 | + expect(mockPool.query).toHaveBeenCalledWith(expect.stringContaining('WHERE u.username = $1'), ['test-user', 3]); |
| 264 | + expect(result).toEqual(mockRecentPosts); |
| 265 | + }); |
| 266 | + |
| 267 | + it('limit 파라미터가 쿼리에 올바르게 적용되어야 한다', async () => { |
| 268 | + mockPool.query.mockResolvedValue(createMockQueryResult(mockRecentPosts)); |
| 269 | + |
| 270 | + await repo.getRecentPosts('test-user', 30, 5); |
| 271 | + |
| 272 | + expect(mockPool.query).toHaveBeenCalledWith(expect.stringContaining('LIMIT $2'), ['test-user', 5]); |
| 273 | + }); |
| 274 | + |
| 275 | + it('released_at 기준 내림차순 정렬이 포함되어야 한다', async () => { |
| 276 | + mockPool.query.mockResolvedValue(createMockQueryResult(mockRecentPosts)); |
| 277 | + |
| 278 | + await repo.getRecentPosts('test-user', 30, 3); |
| 279 | + |
| 280 | + expect(mockPool.query).toHaveBeenCalledWith( |
| 281 | + expect.stringContaining('ORDER BY p.released_at DESC'), |
| 282 | + expect.anything(), |
| 283 | + ); |
| 284 | + }); |
| 285 | + |
| 286 | + it('CTE 쿼리가 포함되어야 한다', async () => { |
| 287 | + mockPool.query.mockResolvedValue(createMockQueryResult(mockRecentPosts)); |
| 288 | + |
| 289 | + await repo.getRecentPosts('test-user', 30, 3); |
| 290 | + |
| 291 | + expect(mockPool.query).toHaveBeenCalledWith(expect.stringContaining('WITH'), expect.anything()); |
| 292 | + }); |
| 293 | + |
| 294 | + it('쿼리 에러 발생 시 DBError를 던져야 한다', async () => { |
| 295 | + mockPool.query.mockRejectedValue(new Error('DB connection failed')); |
| 296 | + |
| 297 | + await expect(repo.getRecentPosts('test-user', 30, 3)).rejects.toThrow(DBError); |
| 298 | + await expect(repo.getRecentPosts('test-user', 30, 3)).rejects.toThrow('최근 게시글 조회 중 문제가 발생했습니다.'); |
| 299 | + }); |
| 300 | + }); |
187 | 301 | }); |
0 commit comments