import { describe, expect, it } from 'vitest' import { projectApplicationCommandContracts } from './projects' const project = { id: 'project-1', name: 'Project', description: '', isExample: false, createdAt: 1, updatedAt: 2} describe('project application command contracts', () => { it('decodes the eight Project command argument and result shapes', () => { const listArgs: [] = [] const listResult = [project] expect(projectApplicationCommandContracts.list.args.parse(listArgs)).toBe(listArgs) expect(projectApplicationCommandContracts.list.result.parse(listResult)).toBe(listResult) expect(projectApplicationCommandContracts.get.args.parse(['project-1'])).toEqual(['project-1']) expect(projectApplicationCommandContracts.get.result.parse(null)).toBeNull() expect(projectApplicationCommandContracts.create.args.parse([{ name: 'Project' }])).toEqual([ { name: 'Project' } ]) expect( projectApplicationCommandContracts.update.args.parse([ { id: 'project-1', name: 'Renamed', expectedUpdatedAt: 2 } ]) ).toEqual([{ id: 'project-1', name: 'Renamed', expectedUpdatedAt: 2 }]) expect( projectApplicationCommandContracts.update.args.parse([ { id: 'project-1', pinned: true, expectedUpdatedAt: 2 } ]) ).toEqual([{ id: 'project-1', pinned: true, expectedUpdatedAt: 2 }]) expect( projectApplicationCommandContracts.create.args.parse([ { name: 'Project', agentContext: 'Always cite DOIs.' } ]) ).toEqual([{ name: 'Project', agentContext: 'Always cite DOIs.' }]) expect( projectApplicationCommandContracts.update.args.parse([ { id: 'project-1', agentContext: 'Always cite DOIs.', expectedUpdatedAt: 2 } ]) ).toEqual([{ id: 'project-1', agentContext: 'Always cite DOIs.', expectedUpdatedAt: 2 }]) expect( projectApplicationCommandContracts.get.result.parse({ ...project, agentContext: 'Always cite DOIs.' }) ).toEqual({ ...project, agentContext: 'Always cite DOIs.' }) expect( projectApplicationCommandContracts.updateArchive.args.parse([ { id: 'project-1', archived: true, expectedArchiveRevision: 0 } ]) ).toEqual([{ id: 'project-1', archived: true, expectedArchiveRevision: 0 }]) expect(projectApplicationCommandContracts.delete.args.parse([{ id: 'project-1' }])).toEqual([ { id: 'project-1' } ]) expect(projectApplicationCommandContracts.delete.result.parse({ status: 'deleted' })).toEqual({ status: 'deleted' }) expect( projectApplicationCommandContracts.delete.result.parse({ status: 'cleanup-pending' }) ).toEqual({ status: 'cleanup-pending' }) const cleanup = [ { projectId: 'project-1', projectName: 'Project', phase: 'retry-scheduled', failureCount: 2, nextRetryAt: 6_000 } ] expect(projectApplicationCommandContracts.listDeletionCleanup.args.parse([])).toEqual([]) expect(projectApplicationCommandContracts.listDeletionCleanup.result.parse(cleanup)).toEqual( cleanup ) expect(projectApplicationCommandContracts.retryDeletionCleanup.args.parse([])).toEqual([]) expect( projectApplicationCommandContracts.retryDeletionCleanup.result.parse(undefined) ).toBeUndefined() }) it('rejects malformed and surplus public fields', () => { expect(() => projectApplicationCommandContracts.create.args.parse([{ name: 42 }])).toThrow() expect(() => projectApplicationCommandContracts.delete.args.parse([ { id: 'project-1', databasePath: '/private/research.db' } ]) ).toThrow() expect(() => projectApplicationCommandContracts.list.result.parse([{ ...project, createdAt: 'today' }]) ).toThrow() expect(() => projectApplicationCommandContracts.listDeletionCleanup.result.parse([ { projectId: 'project-1', phase: 'retry-scheduled', failureCount: 1, nextRetryAt: 6_000, error: '/private/research.db' } ]) ).toThrow() }) it('rejects an agent context beyond the 16000 character limit', () => { const oversized = 'x'.repeat(16001) expect(() => projectApplicationCommandContracts.create.args.parse([ { name: 'Project', agentContext: oversized } ]) ).toThrow() }) it('requires an update baseline and enforces Project name and description limits', () => { expect(() => projectApplicationCommandContracts.update.args.parse([{ id: 'project-1', name: 'Renamed' }]) ).toThrow() expect(() => projectApplicationCommandContracts.create.args.parse([{ name: 'x'.repeat(201) }]) ).toThrow() expect(() => projectApplicationCommandContracts.create.args.parse([ { name: 'Project', description: 'x'.repeat(1001) } ]) ).toThrow() expect(() => projectApplicationCommandContracts.update.args.parse([ { id: 'project-1', name: 'x'.repeat(201), expectedUpdatedAt: 2 } ]) ).toThrow() expect(() => projectApplicationCommandContracts.update.args.parse([ { id: 'project-1', description: 'x'.repeat(1001), expectedUpdatedAt: 2 } ]) ).toThrow() expect( projectApplicationCommandContracts.create.args.parse([ { name: 'x'.repeat(200), description: 'x'.repeat(1000) } ]) ).toEqual([{ name: 'x'.repeat(200), description: 'x'.repeat(1000) }]) }) it('keeps historical projects above the new write limits readable', () => { const historicalProject = { ...project, name: 'x'.repeat(201), description: 'x'.repeat(1001) } expect(projectApplicationCommandContracts.list.result.parse([historicalProject])).toEqual([ historicalProject ]) })})
import { describe, expect, it } from 'vitest'
import { projectApplicationCommandContracts } from './projects'
const project = {
id: 'project-1',
name: 'Project',
description: '',
isExample: false,
createdAt: 1,
updatedAt: 2
}
describe('project application command contracts', () => {
it('decodes the eight Project command argument and result shapes', () => {
const listArgs: [] = []
const listResult = [project]
expect(projectApplicationCommandContracts.list.args.parse(listArgs)).toBe(listArgs)
expect(projectApplicationCommandContracts.list.result.parse(listResult)).toBe(listResult)
expect(projectApplicationCommandContracts.get.args.parse(['project-1'])).toEqual(['project-1'])
expect(projectApplicationCommandContracts.get.result.parse(null)).toBeNull()
expect(projectApplicationCommandContracts.create.args.parse([{ name: 'Project' }])).toEqual([
{ name: 'Project' }
])
expect(
projectApplicationCommandContracts.update.args.parse([
{ id: 'project-1', name: 'Renamed', expectedUpdatedAt: 2 }
])
).toEqual([{ id: 'project-1', name: 'Renamed', expectedUpdatedAt: 2 }])
expect(
projectApplicationCommandContracts.update.args.parse([
{ id: 'project-1', pinned: true, expectedUpdatedAt: 2 }
])
).toEqual([{ id: 'project-1', pinned: true, expectedUpdatedAt: 2 }])
expect(
projectApplicationCommandContracts.create.args.parse([
{ name: 'Project', agentContext: 'Always cite DOIs.' }
])
).toEqual([{ name: 'Project', agentContext: 'Always cite DOIs.' }])
expect(
projectApplicationCommandContracts.update.args.parse([
{ id: 'project-1', agentContext: 'Always cite DOIs.', expectedUpdatedAt: 2 }
])
).toEqual([{ id: 'project-1', agentContext: 'Always cite DOIs.', expectedUpdatedAt: 2 }])
expect(
projectApplicationCommandContracts.get.result.parse({
...project,
agentContext: 'Always cite DOIs.'
})
).toEqual({ ...project, agentContext: 'Always cite DOIs.' })
expect(
projectApplicationCommandContracts.updateArchive.args.parse([
{ id: 'project-1', archived: true, expectedArchiveRevision: 0 }
])
).toEqual([{ id: 'project-1', archived: true, expectedArchiveRevision: 0 }])
expect(projectApplicationCommandContracts.delete.args.parse([{ id: 'project-1' }])).toEqual([
{ id: 'project-1' }
])
expect(projectApplicationCommandContracts.delete.result.parse({ status: 'deleted' })).toEqual({
status: 'deleted'
})
expect(
projectApplicationCommandContracts.delete.result.parse({ status: 'cleanup-pending' })
).toEqual({ status: 'cleanup-pending' })
const cleanup = [
{
projectId: 'project-1',
projectName: 'Project',
phase: 'retry-scheduled',
failureCount: 2,
nextRetryAt: 6_000
}
]
expect(projectApplicationCommandContracts.listDeletionCleanup.args.parse([])).toEqual([])
expect(projectApplicationCommandContracts.listDeletionCleanup.result.parse(cleanup)).toEqual(
cleanup
)
expect(projectApplicationCommandContracts.retryDeletionCleanup.args.parse([])).toEqual([])
expect(
projectApplicationCommandContracts.retryDeletionCleanup.result.parse(undefined)
).toBeUndefined()
})
it('rejects malformed and surplus public fields', () => {
expect(() => projectApplicationCommandContracts.create.args.parse([{ name: 42 }])).toThrow()
expect(() =>
projectApplicationCommandContracts.delete.args.parse([
{ id: 'project-1', databasePath: '/private/research.db' }
])
).toThrow()
expect(() =>
projectApplicationCommandContracts.list.result.parse([{ ...project, createdAt: 'today' }])
).toThrow()
expect(() =>
projectApplicationCommandContracts.listDeletionCleanup.result.parse([
{
projectId: 'project-1',
phase: 'retry-scheduled',
failureCount: 1,
nextRetryAt: 6_000,
error: '/private/research.db'
}
])
).toThrow()
})
it('rejects an agent context beyond the 16000 character limit', () => {
const oversized = 'x'.repeat(16001)
expect(() =>
projectApplicationCommandContracts.create.args.parse([
{ name: 'Project', agentContext: oversized }
])
).toThrow()
})
it('requires an update baseline and enforces Project name and description limits', () => {
expect(() =>
projectApplicationCommandContracts.update.args.parse([{ id: 'project-1', name: 'Renamed' }])
).toThrow()
expect(() =>
projectApplicationCommandContracts.create.args.parse([{ name: 'x'.repeat(201) }])
).toThrow()
expect(() =>
projectApplicationCommandContracts.create.args.parse([
{ name: 'Project', description: 'x'.repeat(1001) }
])
).toThrow()
expect(() =>
projectApplicationCommandContracts.update.args.parse([
{ id: 'project-1', name: 'x'.repeat(201), expectedUpdatedAt: 2 }
])
).toThrow()
expect(() =>
projectApplicationCommandContracts.update.args.parse([
{ id: 'project-1', description: 'x'.repeat(1001), expectedUpdatedAt: 2 }
])
).toThrow()
expect(
projectApplicationCommandContracts.create.args.parse([
{ name: 'x'.repeat(200), description: 'x'.repeat(1000) }
])
).toEqual([{ name: 'x'.repeat(200), description: 'x'.repeat(1000) }])
})
it('keeps historical projects above the new write limits readable', () => {
const historicalProject = {
...project,
name: 'x'.repeat(201),
description: 'x'.repeat(1001)
}
expect(projectApplicationCommandContracts.list.result.parse([historicalProject])).toEqual([
historicalProject
])
})
})