v1.12.22 관리자 멤버 댓글 목록 및 최근 댓글 확인 추가

This commit is contained in:
2026-10-21 15:34:31 +09:00
parent 6886c57d04
commit c8e00c1190
15 changed files with 255 additions and 9 deletions

View File

@@ -229,6 +229,47 @@ test('CSP 보고는 민감 URL을 제거해 같은 차원으로 집계한다', a
expect(JSON.stringify(rows[0])).not.toContain('/private/one.js')
})
test('관리자 댓글 조회는 인증·입력 검증과 멤버별 페이지·답글·댓글 수를 제공한다', async ({ request: anonymousApi }) => {
expect((await anonymousApi.get('/admin/api/comments')).status()).toBe(401)
for (const query of ['page=0', 'pageSize=101', 'memberId=invalid']) {
expect((await adminApi.get(`/admin/api/comments?${query}`)).status()).toBe(400)
}
const [post] = await database`SELECT id, slug, title FROM posts LIMIT 1`
const [other] = await database`
INSERT INTO users (username, email, password_hash)
VALUES ('댓글 테스트', 'comment-test@example.com', 'unused') RETURNING id
`
try {
const [parent] = await database`
INSERT INTO comments (post_id, user_id, body)
VALUES (${post.id}, ${other.id}, '다른 회원 댓글') RETURNING id
`
await database`
INSERT INTO comments (post_id, user_id, parent_id, body, created_at)
VALUES (${post.id}, ${adminUser.userId}, ${parent.id}, '회원 답글', now() + interval '1 second'),
(${post.id}, ${adminUser.userId}, NULL, '회원 댓글', now())
`
const first = await (await adminApi.get(`/admin/api/comments?memberId=${adminUser.userId}&pageSize=1`)).json()
expect(first.pagination).toMatchObject({ page: 1, total: 2, pageCount: 2 })
expect(first.items[0]).toMatchObject({ body: '회원 답글', parentId: parent.id, postId: post.id, postTitle: post.title, postSlug: post.slug })
const second = await (await adminApi.get(`/admin/api/comments?memberId=${adminUser.userId}&pageSize=1&page=2`)).json()
expect(second.items[0].body).toBe('회원 댓글')
const last = await (await adminApi.get(`/admin/api/comments?memberId=${adminUser.userId}&pageSize=1&page=999`)).json()
expect(last.pagination.page).toBe(2)
const recent = await (await adminApi.get('/admin/api/comments?pageSize=5')).json()
expect(recent.items[0].body).toBe('회원 답글')
const posts = await (await adminApi.get('/admin/api/posts?paged=true')).json()
expect(posts.items.find((item) => item.id === post.id).commentCount).toBe(3)
await database`DELETE FROM comments WHERE post_id = ${post.id}`
const empty = await (await adminApi.get(`/admin/api/comments?memberId=${adminUser.userId}`)).json()
expect(empty.pagination.total).toBe(0)
expect(empty.items).toEqual([])
} finally {
await database`DELETE FROM comments WHERE post_id = ${post.id}`
await database`DELETE FROM users WHERE id = ${other.id}`
}
})
test('CSP 수집은 잘못된 형식과 32KB 초과 본문을 거부한다', async ({ request: anonymousApi }) => {
const unsupportedResponse = await anonymousApi.post('/api/csp-report', {
headers: {

View File

@@ -0,0 +1,45 @@
import { expect, test } from '@playwright/test'
import postgres from 'postgres'
test('멤버 댓글 페이지 이동·게시물 링크와 대시보드 최근 댓글을 표시한다', async ({ page }) => {
const database = postgres(process.env.DATABASE_URL, { max: 1 })
let postId
try {
const login = await page.request.post('/admin/api/auth/login', {
data: { email: 'playwright-admin@example.com', password: 'playwright-admin-password' }
})
expect(login.ok()).toBeTruthy()
const admin = await login.json()
const created = await page.request.post('/admin/api/posts', {
data: { title: '댓글 화면 검증 게시물', slug: 'comments-ui-test', content: '본문', status: 'published', tags: [], tagNames: {} }
})
expect(created.ok()).toBeTruthy()
postId = (await created.json()).id
for (let index = 0; index < 11; index += 1) {
await database`
INSERT INTO comments (post_id, user_id, body, created_at)
VALUES (${postId}, ${admin.userId}, ${`화면 댓글 ${index}`}, now() + ${index} * interval '1 second')
`
}
await page.goto(`/admin/members/${admin.userId}`)
await page.waitForLoadState('networkidle')
const list = page.locator('.admin-comment-list')
await expect(list.locator('.admin-comment-list__item')).toHaveCount(10)
await expect(list.locator('.admin-comment-list__body').first()).toHaveText('화면 댓글 10')
await expect(list.getByRole('link', { name: '게시물 보기' }).first()).toHaveAttribute('href', '/post/comments-ui-test#comments')
await list.getByRole('button', { name: '다음' }).click()
await expect(list.locator('.admin-comment-list__item')).toHaveCount(1)
await expect(list.locator('.admin-comment-list__body')).toHaveText('화면 댓글 0')
await list.getByRole('button', { name: '이전' }).click()
await expect(list.locator('.admin-comment-list__item')).toHaveCount(10)
await page.goto('/admin')
await expect(page.locator('.admin-comment-list__item')).toHaveCount(5)
await expect(page.locator('.admin-comment-list__body').first()).toHaveText('화면 댓글 10')
await page.goto('/admin/posts')
const row = page.locator('.admin-posts__row').filter({ hasText: '댓글 화면 검증 게시물' })
await expect(row.locator('.admin-posts__comment-count')).toHaveText('11')
} finally {
if (postId) await database`DELETE FROM posts WHERE id = ${postId}`
await database.end()
}
})