refactor(board): atomic delete + query limit + composable cleanup
Delete uses findOneAndDelete with author match (no TOCTOU window); existence check only runs on miss to distinguish 403 vs 404. Posts list capped at 200. Drop unused resolveTagChannel and refreshParams; route slack URL building through the composable's slackUrl helper.
This commit is contained in:
parent
d1a1484daf
commit
28040f44f4
7 changed files with 30 additions and 54 deletions
|
|
@ -4,9 +4,11 @@ import { setResponseStatus } from 'h3'
|
|||
vi.stubGlobal('setResponseStatus', setResponseStatus)
|
||||
|
||||
// --- Mocks ---
|
||||
const { mockFind, mockFindById, mockSaveInstance } = vi.hoisted(() => ({
|
||||
const { mockFind, mockFindById, mockFindOneAndDelete, mockExists, mockSaveInstance } = vi.hoisted(() => ({
|
||||
mockFind: vi.fn(),
|
||||
mockFindById: vi.fn(),
|
||||
mockFindOneAndDelete: vi.fn(),
|
||||
mockExists: vi.fn(),
|
||||
mockSaveInstance: vi.fn(),
|
||||
}))
|
||||
|
||||
|
|
@ -25,6 +27,8 @@ vi.mock('../../../server/models/boardPost.js', () => {
|
|||
}
|
||||
BoardPost.find = mockFind
|
||||
BoardPost.findById = mockFindById
|
||||
BoardPost.findOneAndDelete = mockFindOneAndDelete
|
||||
BoardPost.exists = mockExists
|
||||
return { default: BoardPost }
|
||||
})
|
||||
|
||||
|
|
@ -63,6 +67,7 @@ const MEMBER_ID = 'member-abc'
|
|||
function buildFindChain(result) {
|
||||
const chain = {
|
||||
sort: vi.fn().mockReturnThis(),
|
||||
limit: vi.fn().mockReturnThis(),
|
||||
populate: vi.fn().mockReturnThis(),
|
||||
lean: vi.fn().mockResolvedValue(result),
|
||||
}
|
||||
|
|
@ -276,39 +281,31 @@ describe('DELETE /api/board/posts/[id]', () => {
|
|||
})
|
||||
|
||||
it('deletes own post', async () => {
|
||||
const deleteOne = vi.fn().mockResolvedValue({})
|
||||
mockFindById.mockResolvedValue({
|
||||
_id: 'post-1',
|
||||
author: { toString: () => MEMBER_ID },
|
||||
deleteOne,
|
||||
})
|
||||
mockFindOneAndDelete.mockResolvedValue({ _id: 'post-1' })
|
||||
|
||||
const event = createMockEvent({ method: 'DELETE', path: '/api/board/posts/post-1' })
|
||||
event.context = { params: { id: 'post-1' } }
|
||||
|
||||
const result = await deleteHandler(event)
|
||||
|
||||
expect(deleteOne).toHaveBeenCalled()
|
||||
expect(mockFindOneAndDelete).toHaveBeenCalledWith({ _id: 'post-1', author: MEMBER_ID })
|
||||
expect(result).toEqual({ success: true })
|
||||
})
|
||||
|
||||
it('rejects deleting another members post with 403', async () => {
|
||||
const deleteOne = vi.fn()
|
||||
mockFindById.mockResolvedValue({
|
||||
_id: 'post-1',
|
||||
author: { toString: () => 'someone-else' },
|
||||
deleteOne,
|
||||
})
|
||||
mockFindOneAndDelete.mockResolvedValue(null)
|
||||
mockExists.mockResolvedValue({ _id: 'post-1' })
|
||||
|
||||
const event = createMockEvent({ method: 'DELETE', path: '/api/board/posts/post-1' })
|
||||
event.context = { params: { id: 'post-1' } }
|
||||
|
||||
await expect(deleteHandler(event)).rejects.toMatchObject({ statusCode: 403 })
|
||||
expect(deleteOne).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('returns 404 when post not found', async () => {
|
||||
mockFindById.mockResolvedValue(null)
|
||||
mockFindOneAndDelete.mockResolvedValue(null)
|
||||
mockExists.mockResolvedValue(null)
|
||||
|
||||
const event = createMockEvent({ method: 'DELETE', path: '/api/board/posts/missing' })
|
||||
event.context = { params: { id: 'missing' } }
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue