Files
template-showcase/src/features/export-notes/api/actions.test.ts
T

64 lines
3.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { beforeEach, describe, expect, it, vi } from 'vitest'
const mocks = vi.hoisted(() => ({
exportToS3: vi.fn(async () => 'exports/visitor-1/now.json' as string | null),
notes: [{ id: 'n-1', title: 'Plan' }],
loggerInfo: vi.fn(),
loggerRecordException: vi.fn(),
log: vi.fn(async () => {}),
captureException: vi.fn(),
}))
vi.mock('@/shared/api', () => ({
db: () => ({
select: () => ({ from: () => ({ where: () => ({ orderBy: () => ({ limit: async () => mocks.notes }) }) }) }),
}),
logger: () => ({ info: mocks.loggerInfo, recordException: mocks.loggerRecordException }),
audit: () => ({ log: mocks.log }),
serverErrors: () => ({ captureException: mocks.captureException }),
}))
vi.mock('@/shared/lib/optional', () => ({ exportToS3: mocks.exportToS3 }))
vi.mock('@/shared/lib/visitor-session', () => ({ ensureVisitor: async () => 'visitor-1' }))
import { exportNotes } from '@/features/export-notes/api/actions'
describe('exportNotes', () => {
beforeEach(() => {
mocks.exportToS3.mockClear()
mocks.loggerInfo.mockClear()
mocks.loggerRecordException.mockClear()
mocks.log.mockClear()
mocks.captureException.mockClear()
})
it('writes the visitor’s own notes to S3 once, logs the outcome, and audits notes.exported with the key', async () => {
const result = await exportNotes(null, new FormData())
expect(mocks.exportToS3).toHaveBeenCalledTimes(1)
expect(mocks.exportToS3).toHaveBeenCalledWith('visitor-1', mocks.notes)
expect(result).toEqual({ ok: true, key: 'exports/visitor-1/now.json', size: expect.any(Number) })
expect(mocks.loggerInfo).toHaveBeenCalledWith('notes.exported', { 'actor.id': 'visitor-1', 'export.count': 1, outcome: 'ok' })
expect(mocks.log).toHaveBeenCalledWith('notes.exported', {
actor: { kind: 'human', id: 'visitor-1' },
target: { id: 'exports/visitor-1/now.json' },
})
expect(mocks.loggerRecordException).not.toHaveBeenCalled()
})
it('reports S3 as not linked, logs it as skipped, and audits nothing, when exportToS3 returns null', async () => {
mocks.exportToS3.mockResolvedValueOnce(null)
const result = await exportNotes(null, new FormData())
expect(result).toEqual({ ok: false, message: 'S3 is not linked; nothing was exported.' })
expect(mocks.loggerInfo).toHaveBeenCalledWith('notes.exported', { 'actor.id': 'visitor-1', 'export.count': 1, outcome: 'skipped' })
expect(mocks.log).not.toHaveBeenCalled()
})
it('reports the error, logs and captures it, when the S3 write throws', async () => {
mocks.exportToS3.mockRejectedValueOnce(new Error('bucket unreachable'))
const result = await exportNotes(null, new FormData())
expect(result).toEqual({ ok: false, message: 'bucket unreachable' })
expect(mocks.loggerRecordException).toHaveBeenCalledWith(expect.any(Error), { 'conusai.action': 'notes.exported', 'actor.id': 'visitor-1' })
expect(mocks.captureException).toHaveBeenCalledTimes(1)
expect(mocks.log).not.toHaveBeenCalled()
})
})