import { describe, expect, it } from 'vitest'; import type { DocumentBrief, FileOut } from '../../types/api'; import { buildDocumentRows, rowBadge } from './documentRows'; const doc = (over: Partial = {}): DocumentBrief => ({ id: 'd1', document_type: 'BALANCE', osv_account_code: null, period_year: 2024, period_quarter: 'Q1', is_cumulative: false, status: 'CONFIRMED', period_check: 'UNKNOWN', counterparty_check: null, locator: null, comments_count: 0, issue_counts: { error: 0, info: 0 }, ...over, }); const file = (over: Partial = {}): FileOut => ({ id: 'f1', legal_entity_id: 'le', original_name: 'Баланс_2024_К1.pdf', file_type: 'PDF', status: 'PROCESSED', version_no: 1, size_bytes: 10, uploaded_at: '2024-03-12T10:00:00Z', consolidation_id: null, job: null, documents: [], ...over, }); describe('buildDocumentRows', () => { it('документ ББ из PDF', () => { const rows = buildDocumentRows([file({ documents: [doc()] })]); expect(rows).toHaveLength(1); expect(rows[0]).toMatchObject({ key: 'd1', documentId: 'd1', name: 'Баланс_2024_К1.pdf', subtitle: 'Бух. баланс · PDF · 12.03.2024', period: '2024 К1', periodCheck: 'UNKNOWN', counterpartyCheck: null, status: 'success', statusLabel: 'Загружен', linkable: true, fileType: 'PDF', }); }); it('два документа одного файла — две строки', () => { const rows = buildDocumentRows([ file({ file_type: 'XLSX', documents: [ doc({ id: 'a', document_type: 'OSV', osv_account_code: '60' }), doc({ id: 'b', document_type: 'OSV', osv_account_code: '62', status: 'NEEDS_CHECK' }), ], }), ]); expect(rows.map((r) => r.key)).toEqual(['a', 'b']); expect(rows[0].subtitle).toBe('ОСВ 60 · XLSX · 12.03.2024'); expect(rows[1].status).toBe('warning'); expect(rows[1].statusLabel).toBe('Требуется проверка'); }); it('файл PROCESSING без документов', () => { const rows = buildDocumentRows([file({ status: 'PROCESSING' })]); expect(rows).toHaveLength(1); expect(rows[0]).toMatchObject({ key: 'f1', documentId: null, period: '—', subtitle: 'PDF · 12.03.2024', status: 'processing', statusLabel: 'Обработка', linkable: false, }); }); it('файл ERROR с причиной', () => { const rows = buildDocumentRows([ file({ status: 'ERROR', job: { id: 'j', status: 'FAILED', error_code: 'E1', error_message: 'сообщение', mapping_error: null, warnings: [], mapping_warnings: [], finished_at: null, } as FileOut['job'], }), ]); expect(rows[0]).toMatchObject({ status: 'error', statusLabel: 'Ошибка', errorReason: 'сообщение', linkable: false }); }); it('расшифровка — «Прочий документ», не кликается', () => { const rows = buildDocumentRows([file({ documents: [doc({ id: 'x', document_type: 'DISCLOSURE' })] })]); expect(rows[0].subtitle).toBe('Прочий документ · PDF · 12.03.2024'); expect(rows[0].linkable).toBe(false); }); it('подпись консолидации добавляется в subtitle последней, null — не добавляется', () => { const rows = buildDocumentRows( [file({ id: 'f1', documents: [doc()] }), file({ id: 'f2', documents: [], consolidation_id: 'c9' })], (f) => (f.id === 'f2' ? 'вне консолидации' : null), ); expect(rows[0].subtitle.endsWith('вне консолидации')).toBe(false); expect(rows[1].subtitle.endsWith(' · вне консолидации')).toBe(true); }); }); const jsonFailed = { code: 'JSON_FAILED', severity: 'error' as const, message: 'm', locator: 'file:x_ofr.json' }; const job = (over: Partial> = {}): FileOut['job'] => ({ id: 'j', status: 'DONE', error_code: null, error_message: null, mapping_error: null, warnings: [], mapping_warnings: [], finished_at: null, ...over, }); describe('buildDocumentRows: предупреждения', () => { it('предупреждения файла попадают в обе строки файла', () => { const rows = buildDocumentRows([ file({ job: job({ warnings: [jsonFailed], mapping_warnings: ['w'] }), documents: [doc({ id: 'a' }), doc({ id: 'b' })] }), ]); expect(rows.map((r) => r.fileWarnings.length)).toEqual([1, 1]); expect(rows[0].mappingWarnings).toEqual(['w']); }); it('файл ERROR без документов', () => { const rows = buildDocumentRows([file({ status: 'ERROR', job: job({ warnings: [jsonFailed] }) })]); expect(rows[0].fileWarnings).toEqual([jsonFailed]); expect(rows[0].issueCounts).toEqual({ error: 0, info: 0 }); }); it('job null — пустые списки; старый формат строк не ломает', () => { expect(buildDocumentRows([file({ documents: [doc()] })])[0]).toMatchObject({ fileWarnings: [], mappingWarnings: [] }); const legacy = buildDocumentRows([file({ job: job({ warnings: ['старое'] as never }), documents: [doc()] })]); expect(legacy[0].fileWarnings[0]).toMatchObject({ code: 'LEGACY_WARNING', message: 'старое' }); }); it('issue_counts документа переносятся в issueCounts', () => { const rows = buildDocumentRows([file({ documents: [doc({ issue_counts: { error: 1, info: 3 } })] })]); expect(rows[0].issueCounts).toEqual({ error: 1, info: 3 }); }); }); describe('rowBadge', () => { const row = (fileWarnings: unknown[], counts: { error: number; info: number }) => buildDocumentRows([file({ job: job({ warnings: fileWarnings as never }), documents: [doc({ issue_counts: counts })] })])[0]; it('только счётчики info', () => { expect(rowBadge(row([], { error: 0, info: 2 }))).toEqual({ count: 2, level: 'info' }); }); it('ошибка файла + info документа', () => { expect(rowBadge(row([jsonFailed], { error: 0, info: 1 }))).toEqual({ count: 2, level: 'error' }); }); it('error документа без файла', () => { expect(rowBadge(row([], { error: 1, info: 0 }))).toMatchObject({ level: 'error' }); }); it('файл без документов: считаются предупреждения файла', () => { const r = buildDocumentRows([file({ status: 'ERROR', job: job({ warnings: [jsonFailed] }) })])[0]; expect(rowBadge(r)).toEqual({ count: 1, level: 'error' }); }); it('нули — null', () => { expect(rowBadge(row([], { error: 0, info: 0 }))).toBeNull(); }); });