From 1b1f2c645ba43add1fa7af0edd0b38989637a80c Mon Sep 17 00:00:00 2001 From: Nuung Date: Sun, 9 Mar 2025 18:30:33 +0900 Subject: [PATCH 1/5] =?UTF-8?q?modify:=20post=20repo=20=EC=97=90=EC=84=9C?= =?UTF-8?q?=20query=20order=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20post=20repo?= =?UTF-8?q?=20=EA=B3=84=EC=B8=B5=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=9E=91=EC=84=B1=20=EC=99=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__test__/sum.test.ts | 3 - .../__test__/post.repository.test.ts | 145 ++++++++++++++++++ src/repositories/post.repository.ts | 1 + 3 files changed, 146 insertions(+), 3 deletions(-) delete mode 100644 src/__test__/sum.test.ts create mode 100644 src/repositories/__test__/post.repository.test.ts diff --git a/src/__test__/sum.test.ts b/src/__test__/sum.test.ts deleted file mode 100644 index 853787b..0000000 --- a/src/__test__/sum.test.ts +++ /dev/null @@ -1,3 +0,0 @@ -test('adds 1 + 2 to equal 3', () => { - expect(1 + 2).toBe(3); -}); diff --git a/src/repositories/__test__/post.repository.test.ts b/src/repositories/__test__/post.repository.test.ts new file mode 100644 index 0000000..3ded9ec --- /dev/null +++ b/src/repositories/__test__/post.repository.test.ts @@ -0,0 +1,145 @@ +import { Pool, QueryResult } from 'pg'; +import { PostRepository } from '@/repositories/post.repository'; +import { DBError } from '@/exception'; + +jest.mock('pg'); + +const mockPool: { + query: jest.Mock>>, unknown[]>; +} = { + query: jest.fn(), +}; + +describe('PostRepository', () => { + let repo: PostRepository; + + beforeEach(() => { + repo = new PostRepository(mockPool as unknown as Pool); + }); + + describe('findPostsByUserId', () => { + it('사용자의 게시글과 nextCursor를 반환해야 한다', async () => { + const mockPosts = [ + { id: 1, post_released_at: '2025-03-01T00:00:00Z', daily_view_count: 10, daily_like_count: 5 }, + { id: 2, post_released_at: '2025-03-02T00:00:00Z', daily_view_count: 20, daily_like_count: 15 }, + ]; + + mockPool.query.mockResolvedValue({ + rows: mockPosts, + rowCount: mockPosts.length, + command: '', + oid: 0, + fields: [], + } as QueryResult); + + const result = await repo.findPostsByUserId(1, undefined, 'released_at', false); + + expect(result.posts).toEqual(mockPosts); + expect(result).toHaveProperty('nextCursor'); + }); + + it('정렬 순서를 보장해야 한다', async () => { + const mockPosts = [ + { id: 2, post_released_at: '2025-03-02T00:00:00Z', daily_view_count: 20, daily_like_count: 15 }, + { id: 1, post_released_at: '2025-03-01T00:00:00Z', daily_view_count: 10, daily_like_count: 5 }, + ]; + + mockPool.query.mockResolvedValue({ + rows: mockPosts, + rowCount: mockPosts.length, + command: '', + oid: 0, + fields: [], + } as QueryResult); + + const result = await repo.findPostsByUserId(1, undefined, 'released_at', false); + expect(result.posts).toEqual(mockPosts); + expect(result.posts[0].id).toBeGreaterThan(result.posts[1].id); + }); + }); + + describe('getTotalPostCounts', () => { + it('사용자의 총 게시글 수를 반환해야 한다', async () => { + mockPool.query.mockResolvedValue({ + rows: [{ count: '10' }], + rowCount: 1, + command: '', + oid: 0, + fields: [], + } as QueryResult); + + const count = await repo.getTotalPostCounts(1); + expect(count).toBe(10); + }); + }); + + describe('에러 발생 시 처리', () => { + it('쿼리 실행 중 에러가 발생하면 DBError를 던져야 한다', async () => { + mockPool.query.mockRejectedValue(new Error('DB connection failed')); + + await expect(repo.findPostsByUserId(1)).rejects.toThrow(DBError); + await expect(repo.getTotalPostCounts(1)).rejects.toThrow(DBError); + }); + }); + + describe('getYesterdayAndTodayViewLikeStats', () => { + it('어제와 오늘의 조회수 및 좋아요 수를 반환해야 한다', async () => { + const mockStats = { + daily_view_count: 20, + daily_like_count: 15, + yesterday_views: 10, + yesterday_likes: 8, + last_updated_date: '2025-03-08T00:00:00Z', + }; + + mockPool.query.mockResolvedValue({ + rows: [mockStats], + rowCount: 1, + command: '', + oid: 0, + fields: [], + } as QueryResult); + + const result = await repo.getYesterdayAndTodayViewLikeStats(1); + expect(result).toEqual(mockStats); + }); + }); + + describe('findPostByPostId', () => { + it('특정 post ID에 대한 통계를 반환해야 한다', async () => { + const mockStats = [ + { date: '2025-03-08T00:00:00Z', daily_view_count: 50, daily_like_count: 30 }, + ]; + + mockPool.query.mockResolvedValue({ + rows: mockStats, + rowCount: mockStats.length, + command: '', + oid: 0, + fields: [], + } as QueryResult); + + const result = await repo.findPostByPostId(1); + expect(result).toEqual(mockStats); + }); + }); + + describe('findPostByPostUUID', () => { + it('특정 post UUID에 대한 통계를 반환해야 한다', async () => { + const mockStats = [ + { date: '2025-03-08T00:00:00Z', daily_view_count: 50, daily_like_count: 30 }, + ]; + + mockPool.query.mockResolvedValue({ + rows: mockStats, + rowCount: mockStats.length, + command: '', + oid: 0, + fields: [], + } as QueryResult); + + const result = await repo.findPostByPostUUID('uuid-1234', '2025-03-01', '2025-03-08'); + expect(result).toEqual(mockStats); + }); + }); +}); diff --git a/src/repositories/post.repository.ts b/src/repositories/post.repository.ts index 1c6116f..78ac5bd 100644 --- a/src/repositories/post.repository.ts +++ b/src/repositories/post.repository.ts @@ -169,6 +169,7 @@ export class PostRepository { pds.daily_like_count FROM posts_postdailystatistics pds WHERE pds.post_id = $1 + ORDER BY pds.date ASC `; const values: (number | string)[] = [postId]; From f41c176c03ab38da8d74b7bece4609b78fb8ab00 Mon Sep 17 00:00:00 2001 From: Nuung Date: Sun, 9 Mar 2025 18:35:33 +0900 Subject: [PATCH 2/5] =?UTF-8?q?feature:=20ci=20=ED=8C=8C=EC=9D=B4=ED=94=84?= =?UTF-8?q?=EB=9D=BC=EC=9D=B8=20=EA=B5=AC=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/test-ci.yaml | 40 ++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .github/workflows/test-ci.yaml diff --git a/.github/workflows/test-ci.yaml b/.github/workflows/test-ci.yaml new file mode 100644 index 0000000..9a231c6 --- /dev/null +++ b/.github/workflows/test-ci.yaml @@ -0,0 +1,40 @@ +name: Test CI + +on: + workflow_dispatch: + push: + branches: ['main'] + pull_request: + branches: + - main + +jobs: + build-and-test: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [18, 19, 20, 21, 22, 23] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: 'pnpm' + + - name: Install pnpm + run: npm install -g pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Run lint + run: pnpm run lint + + - name: Run tests + run: pnpm run test + + - name: Run build + run: pnpm run build From a0acc5d449a6be7da50d564fa9fed288012db89a Mon Sep 17 00:00:00 2001 From: Nuung Date: Sun, 9 Mar 2025 18:40:40 +0900 Subject: [PATCH 3/5] =?UTF-8?q?feature:=20ci=20=ED=8C=8C=EC=9D=B4=ED=94=84?= =?UTF-8?q?=EB=9D=BC=EC=9D=B8=20=EA=B5=AC=EC=84=B1=20=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/test-ci.yaml | 8 +++++++- README.md | 3 ++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-ci.yaml b/.github/workflows/test-ci.yaml index 9a231c6..369dc1f 100644 --- a/.github/workflows/test-ci.yaml +++ b/.github/workflows/test-ci.yaml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [18, 19, 20, 21, 22, 23] + node-version: [20, 21, 22, 23] steps: - name: Checkout repository uses: actions/checkout@v4 @@ -24,9 +24,15 @@ jobs: node-version: ${{ matrix.node-version }} cache: 'pnpm' + - name: Enable Corepack (for pnpm) + run: corepack enable + - name: Install pnpm run: npm install -g pnpm + - name: Verify pnpm installation + run: pnpm --version + - name: Install dependencies run: pnpm install --frozen-lockfile diff --git a/README.md b/README.md index 30fd4e3..f0547d5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Velog Dashboard Project -- 백엔드 API 서버 +- Velog dashboard V2 백엔드, API 서버 +- ***`node 20+`*** ## Project Setup Guide From ff444bc1d9437966231873a5a56633b58a9d670e Mon Sep 17 00:00:00 2001 From: Nuung Date: Sun, 9 Mar 2025 18:44:37 +0900 Subject: [PATCH 4/5] =?UTF-8?q?modify:=20ci=20=ED=8C=8C=EC=9D=B4=ED=94=84?= =?UTF-8?q?=EB=9D=BC=EC=9D=B8=20=EA=B5=AC=EC=84=B1=20=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8=20v3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/test-ci.yaml | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test-ci.yaml b/.github/workflows/test-ci.yaml index 369dc1f..885777c 100644 --- a/.github/workflows/test-ci.yaml +++ b/.github/workflows/test-ci.yaml @@ -5,8 +5,7 @@ on: push: branches: ['main'] pull_request: - branches: - - main + branches: ['main'] jobs: build-and-test: @@ -14,6 +13,8 @@ jobs: strategy: matrix: node-version: [20, 21, 22, 23] + fail-fast: false # 한 버전 실패 시 전체 중단 방지 + steps: - name: Checkout repository uses: actions/checkout@v4 @@ -22,16 +23,29 @@ jobs: uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - cache: 'pnpm' - - name: Enable Corepack (for pnpm) + - name: Enable Corepack run: corepack enable - - name: Install pnpm - run: npm install -g pnpm + - name: Setup pnpm + uses: pnpm/action-setup@v2 + with: + version: 9 + run_install: false - - name: Verify pnpm installation - run: pnpm --version + - name: Get pnpm store directory + id: pnpm-cache + shell: bash + run: | + echo "store-path=$(pnpm store path)" >> $GITHUB_OUTPUT + + - name: Setup pnpm cache + uses: actions/cache@v3 + with: + path: ${{ steps.pnpm-cache.outputs.store-path }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- - name: Install dependencies run: pnpm install --frozen-lockfile @@ -43,4 +57,4 @@ jobs: run: pnpm run test - name: Run build - run: pnpm run build + run: pnpm run build \ No newline at end of file From a6612de9bf8cd7af34a9a7a3299b76daade03864 Mon Sep 17 00:00:00 2001 From: Nuung Date: Sun, 9 Mar 2025 18:47:24 +0900 Subject: [PATCH 5/5] =?UTF-8?q?modify:=20=EC=BD=94=EB=93=9C=EB=A0=88?= =?UTF-8?q?=EB=B9=97=EC=9D=B4=20actions/cache=20=EC=BA=90=EC=8B=9C=20?= =?UTF-8?q?=EB=B2=84=EC=A0=84=204=20=EC=93=B0=EB=9D=BC=EA=B3=A0=20?= =?UTF-8?q?=ED=95=98=EB=84=A4=EC=9A=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/test-ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-ci.yaml b/.github/workflows/test-ci.yaml index 885777c..08c7bfc 100644 --- a/.github/workflows/test-ci.yaml +++ b/.github/workflows/test-ci.yaml @@ -40,7 +40,7 @@ jobs: echo "store-path=$(pnpm store path)" >> $GITHUB_OUTPUT - name: Setup pnpm cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ steps.pnpm-cache.outputs.store-path }} key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}