import { describe, expect, it } from 'vitest'; import { exportValidatedOutput } from '../../src/document-workbench/export-validated-output'; const fields = [ { name: 'name', type: 'string' as const, required: true }, { name: 'count', type: 'integer' as const, required: true }, ]; const sources = [{ id: 'source', text: 'Acme, "North"\n-2' }]; const evidence = [ { field: 'name', sourceId: 'source', start: 0, end: 13, transform: 'exact' as const }, { field: 'count', sourceId: 'source', start: 14, end: 16, transform: 'integer' as const }, ]; const input = { fields, sources, evidence, value: { name: 'Acme, "North"', count: -2 } }; describe('validated document exports', () => { it('preserves JSON values and produces correctly escaped, ordered CSV', () => { const json = exportValidatedOutput({ ...input, format: 'json' }); expect(json.status).toBe('ready'); if (json.status === 'ready') expect(JSON.parse(json.content)).toEqual(input.value); expect(exportValidatedOutput({ ...input, format: 'csv' })).toEqual({ status: 'ready', mediaType: 'text/csv', content: '"name","count"\r\n"Acme, ""North""","-2"\r\n', }); }); it('revalidates evidence and refuses unsupported or absent data', () => { expect(exportValidatedOutput({ ...input, value: { ...input.value, name: 'invented' }, format: 'json' }).status).toBe('needs_review'); expect(exportValidatedOutput({ ...input, evidence: [], format: 'csv' }).status).toBe('needs_review'); const optional = { ...input, fields: [...fields, { name: 'note', type: 'string' as const, required: false }] }; expect(exportValidatedOutput({ ...optional, format: 'csv' })).toMatchObject({ status: 'needs_review', issues: [{ field: 'note', code: 'csv_missing_value' }] }); expect(exportValidatedOutput({ ...optional, format: 'json' }).status).toBe('ready'); expect(exportValidatedOutput({ ...input, format: 'xml' as 'json' })).toMatchObject({ status: 'needs_review', issues: [{ code: 'unsupported_export_format' }] }); }); it('rejects formula-like CSV values and headers without changing JSON', () => { const bad = { fields: [{ name: 'cell', type: 'string' as const, required: true }], sources: [{ id: 's', text: ' =1+1' }], evidence: [{ field: 'cell', sourceId: 's', start: 0, end: 6, transform: 'exact' as const }], value: { cell: ' =1+1' } }; expect(exportValidatedOutput({ ...bad, format: 'csv' })).toMatchObject({ status: 'needs_review', issues: [{ code: 'csv_unsafe_cell' }] }); expect(exportValidatedOutput({ ...bad, format: 'json' }).status).toBe('ready'); const header = { fields: [{ name: '=header', type: 'string' as const, required: true }], sources: [{ id: 's', text: 'safe' }], evidence: [{ field: '=header', sourceId: 's', start: 0, end: 4, transform: 'exact' as const }], value: { '=header': 'safe' } }; expect(exportValidatedOutput({ ...header, format: 'csv' })).toMatchObject({ status: 'needs_review', issues: [{ code: 'csv_unsafe_cell' }] }); }); it('preserves a source-backed decimal as text without floating-point rounding', () => { const text = '9007199254740993.20'; const decimal = { fields: [{ name: 'amount', type: 'decimal' as const, required: true }], sources: [{ id: 's', text }], evidence: [{ field: 'amount', sourceId: 's', start: 0, end: text.length, transform: 'decimal' as const }], value: { amount: text } }; const result = exportValidatedOutput({ ...decimal, format: 'json' }); expect(result.status).toBe('ready'); if (result.status === 'ready') expect(JSON.parse(result.content)).toEqual({ amount: text }); }); });