Skip to content

Commit f32c459

Browse files
committed
feature: 테스트 케이스 강화
1 parent a9d6845 commit f32c459

File tree

2 files changed

+182
-0
lines changed

2 files changed

+182
-0
lines changed

src/repositories/__test__/post.repo.integration.test.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,87 @@ describe('PostRepository 통합 테스트', () => {
262262
});
263263
});
264264

265+
/**
266+
* findPostsByUserIdWithGrowthMetrics 테스트
267+
*/
268+
describe('findPostsByUserIdWithGrowthMetrics', () => {
269+
it('트래픽 성장률 데이터를 포함한 게시물 목록을 조회할 수 있어야 한다', async () => {
270+
const result = await repo.findPostsByUserIdWithGrowthMetrics(TEST_DATA.USER_ID);
271+
272+
expect(result).toBeDefined();
273+
expect(result).toHaveProperty('posts');
274+
expect(result).toHaveProperty('nextCursor');
275+
expect(Array.isArray(result.posts)).toBe(true);
276+
expect(result.posts[0]).toHaveProperty('view_growth');
277+
expect(result.posts[0]).toHaveProperty('like_growth');
278+
});
279+
280+
it('트래픽 성장률을 기준으로 내림차순 정렬해야 한다', async () => {
281+
const result = await repo.findPostsByUserIdWithGrowthMetrics(TEST_DATA.USER_ID);
282+
283+
// 결과가 2개 이상인 경우만 의미 있는 검증 가능
284+
if (result.posts.length < 2) {
285+
logger.info('트래픽 성장률 정렬 테스트를 위한 충분한 데이터가 없습니다.');
286+
return;
287+
}
288+
289+
// 내림차순 정렬 확인
290+
const isSortedByGrowth = result.posts.every((post, index) => {
291+
if (index === 0) return true;
292+
return post.view_growth <= result.posts[index - 1].view_growth;
293+
});
294+
expect(isSortedByGrowth).toBe(true);
295+
});
296+
297+
it('오름차순 정렬이 제대로 동작해야 한다', async () => {
298+
const result = await repo.findPostsByUserIdWithGrowthMetrics(TEST_DATA.USER_ID, undefined, true);
299+
300+
expect(result).toBeDefined();
301+
expect(Array.isArray(result.posts)).toBe(true);
302+
303+
// 결과가 2개 이상인 경우에만 검증
304+
if (result.posts.length >= 2) {
305+
const isSortedAsc = result.posts.every((post, index) => {
306+
if (index === 0) return true;
307+
return post.view_growth >= result.posts[index - 1].view_growth;
308+
});
309+
310+
// eslint-disable-next-line jest/no-conditional-expect
311+
expect(isSortedAsc).toBe(true);
312+
}
313+
});
314+
315+
it('페이지네이션을 위한 nextCursor를 제공해야 한다', async () => {
316+
// 먼저 제한된 수의 결과를 가져옴
317+
const limitedResult = await repo.findPostsByUserIdWithGrowthMetrics(TEST_DATA.USER_ID, undefined, false, 1);
318+
319+
// 최소 2개 이상의 게시물이 있으면 nextCursor가 있어야 함
320+
const totalCount = await repo.getTotalPostCounts(TEST_DATA.USER_ID);
321+
322+
if (totalCount <= 1 || limitedResult.posts.length !== 1) {
323+
logger.info('트래픽 성장률 페이지네이션 테스트를 위한 충분한 데이터가 없습니다.');
324+
return;
325+
}
326+
327+
expect(limitedResult.nextCursor).toBeTruthy();
328+
329+
// nextCursor를 사용한 두 번째 쿼리
330+
const secondPage = await repo.findPostsByUserIdWithGrowthMetrics(
331+
TEST_DATA.USER_ID,
332+
limitedResult.nextCursor || undefined
333+
);
334+
335+
expect(secondPage.posts).toBeDefined();
336+
expect(Array.isArray(secondPage.posts)).toBe(true);
337+
338+
// 첫 페이지와 두 번째 페이지의 항목은 중복되지 않아야 함
339+
const page1Ids = limitedResult.posts.map(post => post.id);
340+
const page2Ids = secondPage.posts.map(post => post.id);
341+
342+
const hasDuplicates = page1Ids.some(id => page2Ids.includes(id));
343+
expect(hasDuplicates).toBe(false);
344+
});
345+
});
265346

266347
/**
267348
* getTotalPostCounts 테스트

src/repositories/__test__/post.repo.test.ts

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,107 @@ describe('PostRepository', () => {
5858
});
5959
});
6060

61+
describe('findPostsByUserIdWithGrowthMetrics', () => {
62+
it('트래픽 성장률 데이터를 포함한 게시글 목록을 반환해야 한다', async () => {
63+
const mockPosts = [
64+
{
65+
id: 1,
66+
post_released_at: '2025-03-01T00:00:00Z',
67+
daily_view_count: 30,
68+
yesterday_daily_view_count: 10,
69+
view_growth: 20,
70+
like_growth: 5
71+
},
72+
{
73+
id: 2,
74+
post_released_at: '2025-03-02T00:00:00Z',
75+
daily_view_count: 25,
76+
yesterday_daily_view_count: 15,
77+
view_growth: 10,
78+
like_growth: 3
79+
},
80+
];
81+
82+
mockPool.query.mockResolvedValue({
83+
rows: mockPosts,
84+
rowCount: mockPosts.length,
85+
command: '',
86+
oid: 0,
87+
fields: [],
88+
} as QueryResult);
89+
90+
const result = await repo.findPostsByUserIdWithGrowthMetrics(1);
91+
92+
expect(result.posts).toEqual(mockPosts);
93+
expect(result).toHaveProperty('nextCursor');
94+
expect(result.posts[0]).toHaveProperty('view_growth');
95+
expect(result.posts[0]).toHaveProperty('like_growth');
96+
});
97+
98+
it('트래픽 성장률을 기준으로 내림차순 정렬해야 한다', async () => {
99+
const mockPosts = [
100+
{
101+
id: 1,
102+
post_released_at: '2025-03-01T00:00:00Z',
103+
daily_view_count: 30,
104+
yesterday_daily_view_count: 10,
105+
view_growth: 20
106+
},
107+
{
108+
id: 2,
109+
post_released_at: '2025-03-02T00:00:00Z',
110+
daily_view_count: 25,
111+
yesterday_daily_view_count: 15,
112+
view_growth: 10
113+
},
114+
];
115+
116+
mockPool.query.mockResolvedValue({
117+
rows: mockPosts,
118+
rowCount: mockPosts.length,
119+
command: '',
120+
oid: 0,
121+
fields: [],
122+
} as QueryResult);
123+
124+
const result = await repo.findPostsByUserIdWithGrowthMetrics(1, undefined, false);
125+
expect(result.posts).toEqual(mockPosts);
126+
expect(result.posts[0].view_growth).toBeGreaterThan(result.posts[1].view_growth);
127+
});
128+
129+
it('커서 기반 페이지네이션이 트래픽 성장률 기준으로 작동해야 한다', async () => {
130+
const mockPosts = [
131+
{
132+
id: 3,
133+
post_released_at: '2025-03-03T00:00:00Z',
134+
daily_view_count: 20,
135+
yesterday_daily_view_count: 15,
136+
view_growth: 5
137+
},
138+
];
139+
140+
mockPool.query.mockResolvedValue({
141+
rows: mockPosts,
142+
rowCount: mockPosts.length,
143+
command: '',
144+
oid: 0,
145+
fields: [],
146+
} as QueryResult);
147+
148+
const result = await repo.findPostsByUserIdWithGrowthMetrics(1, "10,2", false);
149+
expect(result.posts).toEqual(mockPosts);
150+
expect(mockPool.query).toHaveBeenCalledWith(
151+
expect.stringContaining("(COALESCE(pds.daily_view_count, 0) - COALESCE(yesterday_stats.daily_view_count, 0)) < $2"),
152+
expect.arrayContaining([1, "10", "2", expect.anything()])
153+
);
154+
});
155+
156+
it('에러 발생 시 DBError를 던져야 한다', async () => {
157+
mockPool.query.mockRejectedValue(new Error('DB connection failed'));
158+
await expect(repo.findPostsByUserIdWithGrowthMetrics(1)).rejects.toThrow(DBError);
159+
});
160+
});
161+
61162
describe('getTotalPostCounts', () => {
62163
it('사용자의 총 게시글 수를 반환해야 한다', async () => {
63164
mockPool.query.mockResolvedValue({

0 commit comments

Comments
 (0)