ConusAI vendor v0.1.0-dev.20261027+9b1131fb

This commit is contained in:
ConusAI vendor v0.1.0-dev.20261027+9b1131fb
2026-10-27 18:05:18 +00:00
commit f23f4e535d
159 changed files with 4293 additions and 0 deletions
@@ -0,0 +1,74 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
const NOTE = '00000000-0000-4000-8000-0000000000aa'
const mocks = vi.hoisted(() => ({
log: vi.fn(async () => {}),
revalidatePath: vi.fn(),
loggerInfo: vi.fn(),
loggerRecordException: vi.fn(),
updated: undefined as { id: string } | undefined,
}))
const fakeDb = {
update: () => ({
set: () => ({
where: () => ({ returning: async () => (mocks.updated ? [mocks.updated] : []) }),
}),
}),
}
vi.mock('next/cache', () => ({ revalidatePath: mocks.revalidatePath }))
vi.mock('@/shared/api', () => ({
db: () => fakeDb,
audit: () => ({ log: mocks.log }),
logger: () => ({ info: mocks.loggerInfo, recordException: mocks.loggerRecordException }),
}))
vi.mock('@/shared/lib/visitor-session', () => ({ ensureVisitor: async () => 'visitor-1' }))
import { updateNote } from '@/features/edit-note/api/actions'
function edit(title: string): FormData {
const data = new FormData()
data.set('title', title)
data.set('body', '')
return data
}
describe('updateNote', () => {
beforeEach(() => {
mocks.log.mockClear()
mocks.revalidatePath.mockClear()
mocks.loggerInfo.mockClear()
mocks.loggerRecordException.mockClear()
mocks.updated = undefined
})
it('audits note.updated exactly once with the actor and the note, and logs the outcome', async () => {
mocks.updated = { id: NOTE }
await updateNote(NOTE, edit('Renamed'))
expect(mocks.log).toHaveBeenCalledTimes(1)
expect(mocks.log).toHaveBeenCalledWith('note.updated', {
actor: { kind: 'human', id: 'visitor-1' },
target: { id: NOTE },
})
expect(mocks.loggerInfo).toHaveBeenCalledTimes(1)
expect(mocks.loggerInfo).toHaveBeenCalledWith('note.updated', { 'note.id': NOTE, outcome: 'ok' })
expect(mocks.loggerRecordException).not.toHaveBeenCalled()
})
it('audits nothing when the visitor did not write the note, and logs it denied', async () => {
await updateNote(NOTE, edit('Renamed'))
expect(mocks.log).not.toHaveBeenCalled()
expect(mocks.loggerInfo).toHaveBeenCalledWith('note.updated', { 'note.id': NOTE, outcome: 'denied' })
expect(mocks.loggerRecordException).not.toHaveBeenCalled()
})
it('logs the failure when the input is invalid', async () => {
await expect(updateNote(NOTE, edit(' '))).rejects.toThrow()
expect(mocks.log).not.toHaveBeenCalled()
expect(mocks.loggerInfo).not.toHaveBeenCalled()
expect(mocks.loggerRecordException).toHaveBeenCalledTimes(1)
expect(mocks.loggerRecordException).toHaveBeenCalledWith(expect.any(Error), { 'conusai.action': 'note.updated', 'note.id': NOTE })
})
})
+26
View File
@@ -0,0 +1,26 @@
'use server'
import { revalidatePath } from 'next/cache'
import { noteId, noteInput, noteRepository } from '@/entities/note'
import { audit, db, logger } from '@/shared/api'
import { ensureVisitor } from '@/shared/lib/visitor-session'
/** Edit a note the visitor wrote. A note they did not write is left untouched. */
export async function updateNote(id: string, form: FormData): Promise<void> {
try {
const key = noteId.parse(id)
const input = noteInput.parse({ title: form.get('title'), body: form.get('body') ?? '' })
const actor = await ensureVisitor()
const note = await noteRepository(db()).update(key, actor, input)
if (!note) {
logger()?.info('note.updated', { 'note.id': key, outcome: 'denied' })
return
}
await audit().log('note.updated', { actor: { kind: 'human', id: actor }, target: { id: note.id } })
logger()?.info('note.updated', { 'note.id': note.id, outcome: 'ok' })
revalidatePath('/')
} catch (error) {
logger()?.recordException(error, { 'conusai.action': 'note.updated', 'note.id': id })
throw error
}
}
+2
View File
@@ -0,0 +1,2 @@
export { updateNote } from './api/actions'
export { EditNoteForm } from './ui/EditNoteForm'
@@ -0,0 +1,18 @@
import type { Note } from '@/entities/note'
import { updateNote } from '@/features/edit-note/api/actions'
export function EditNoteForm({ note }: { note: Note }) {
return (
<form action={updateNote.bind(null, note.id)}>
<label>
Title
<input name="title" defaultValue={note.title} required maxLength={200} />
</label>
<label>
Body
<textarea name="body" rows={3} defaultValue={note.body} />
</label>
<button type="submit">Save</button>
</form>
)
}