import { describe, expect, test } from 'bun:test' const vaultMetadata = { originalFileName: 'invoice.pdf', mimeType: 'application/pdf' } async function lookup() { return vaultMetadata } async function failingLookup(): Promise { throw new Error('vault unavailable') } describe('test case file names', () => { test('resolves the real file name from the vault when none was supplied', async () => { const { createTestCasePayload, resolveTestCaseFileNames } = await import('./test-cases.ts') const payload = createTestCasePayload('prompt-id', { document: 'vault://abc' }) expect(payload.files![0]!.name).toBe('document') const resolved = await resolveTestCaseFileNames(payload, lookup) expect(resolved.files![0]!.name).toBe('invoice.pdf') expect(resolved.files![0]!.mimeType).toBe('application/pdf') }) test('rewrites the rich content file node to match', async () => { const { createTestCasePayload, resolveTestCaseFileNames } = await import('./test-cases.ts') const resolved = await resolveTestCaseFileNames( createTestCasePayload('prompt-id', { document: 'vault://abc' }), lookup, ) expect(resolved.variablesRichContent!.document).toContain('filename="invoice.pdf"') expect(resolved.variablesRichContent!.document).toContain('filetype="application/pdf"') }) test('keeps an explicitly supplied file name', async () => { const { createTestCasePayload, resolveTestCaseFileNames } = await import('./test-cases.ts') const resolved = await resolveTestCaseFileNames( createTestCasePayload('prompt-id', { document: 'vault://abc' }, { fileNames: { document: 'renamed.pdf' }, }), lookup, ) expect(resolved.files![0]!.name).toBe('renamed.pdf') }) test('leaves a payload without files untouched', async () => { const { createTestCasePayload, resolveTestCaseFileNames } = await import('./test-cases.ts') const payload = createTestCasePayload('prompt-id', { topic: 'invoices' }) expect(await resolveTestCaseFileNames(payload, failingLookup)).toBe(payload) }) test('falls back to the original payload when the vault lookup fails', async () => { const { createTestCasePayload, resolveTestCaseFileNames } = await import('./test-cases.ts') const resolved = await resolveTestCaseFileNames( createTestCasePayload('prompt-id', { document: 'vault://abc' }), failingLookup, ) expect(resolved.files![0]!.name).toBe('document') }) test('resolves only the files that need it', async () => { const { createTestCasePayload, resolveTestCaseFileNames } = await import('./test-cases.ts') const resolved = await resolveTestCaseFileNames( createTestCasePayload('prompt-id', { report: 'vault://abc', scan: 'vault://def' }, { fileNames: { report: 'report.pdf' }, }), lookup, ) expect(resolved.files!.map(file => file.name)).toEqual(['report.pdf', 'invoice.pdf']) }) }) describe('toSafeFileName', () => { test('keeps an ordinary name', async () => { const { toSafeFileName } = await import('./test-cases.ts') expect(toSafeFileName('invoice.pdf')).toBe('invoice.pdf') }) test('strips traversal segments', async () => { const { toSafeFileName } = await import('./test-cases.ts') expect(toSafeFileName('../../../etc/passwd')).toBe('passwd') expect(toSafeFileName('..')).toBe('file') }) test('strips an absolute path down to its base', async () => { const { toSafeFileName } = await import('./test-cases.ts') expect(toSafeFileName('/etc/passwd')).toBe('passwd') }) test('strips windows separators too', async () => { const { toSafeFileName } = await import('./test-cases.ts') expect(toSafeFileName('..\\..\\windows\\system32\\cmd.exe')).toBe('cmd.exe') }) test('never returns a hidden file', async () => { const { toSafeFileName } = await import('./test-cases.ts') expect(toSafeFileName('.bashrc')).toBe('bashrc') }) test('falls back when nothing usable remains', async () => { const { toSafeFileName } = await import('./test-cases.ts') expect(toSafeFileName('', 'file-0')).toBe('file-0') expect(toSafeFileName('/', 'file-0')).toBe('file-0') expect(toSafeFileName(' ', 'file-0')).toBe('file-0') }) }) describe('toUniqueFileName', () => { test('leaves a free name alone', async () => { const { toUniqueFileName } = await import('./test-cases.ts') expect(toUniqueFileName('invoice.pdf', new Set())).toBe('invoice.pdf') }) test('disambiguates a collision while keeping the extension', async () => { const { toUniqueFileName } = await import('./test-cases.ts') expect(toUniqueFileName('invoice.pdf', new Set(['invoice.pdf']))).toBe('invoice-1.pdf') }) test('keeps counting past the first collision', async () => { const { toUniqueFileName } = await import('./test-cases.ts') expect(toUniqueFileName('invoice.pdf', new Set(['invoice.pdf', 'invoice-1.pdf']))) .toBe('invoice-2.pdf') }) test('handles a name with no extension', async () => { const { toUniqueFileName } = await import('./test-cases.ts') expect(toUniqueFileName('report', new Set(['report']))).toBe('report-1') }) test('does not mistake a leading dot for an extension boundary', async () => { const { toUniqueFileName } = await import('./test-cases.ts') expect(toUniqueFileName('.env', new Set(['.env']))).toBe('.env-1') }) test('preserves a multi-dot name', async () => { const { toUniqueFileName } = await import('./test-cases.ts') expect(toUniqueFileName('nota.fiscal.pdf', new Set(['nota.fiscal.pdf']))) .toBe('nota.fiscal-1.pdf') }) test('two files reducing to the same basename both survive', async () => { const { toSafeFileName, toUniqueFileName } = await import('./test-cases.ts') const taken = new Set() const names = ['a/invoice.pdf', 'b/invoice.pdf'].map((raw) => { const name = toUniqueFileName(toSafeFileName(raw), taken) taken.add(name) return name }) expect(names).toEqual(['invoice.pdf', 'invoice-1.pdf']) expect(new Set(names).size).toBe(2) }) }) describe('toUniqueFileName on a case-insensitive filesystem', () => { test('treats names differing only in case as the same file', async () => { const { toUniqueFileName } = await import('./test-cases.ts') expect(toUniqueFileName('Invoice.PDF', new Set(['invoice.pdf']))).toBe('Invoice-1.PDF') }) test('keeps the original casing of the name it returns', async () => { const { toUniqueFileName } = await import('./test-cases.ts') expect(toUniqueFileName('Invoice.PDF', new Set())).toBe('Invoice.PDF') }) test('skips a suffix already taken in another casing', async () => { const { toUniqueFileName } = await import('./test-cases.ts') expect(toUniqueFileName('doc.txt', new Set(['DOC.TXT', 'Doc-1.txt']))).toBe('doc-2.txt') }) test('case-variant attachments all survive', async () => { const { toUniqueFileName } = await import('./test-cases.ts') const taken = new Set() const names = ['invoice.pdf', 'Invoice.PDF', 'INVOICE.pdf'].map((raw) => { const name = toUniqueFileName(raw, taken) taken.add(name) return name }) expect(new Set(names.map(n => n.toLowerCase())).size).toBe(3) }) })