Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BJKMLHdxMwSUeYcdkwqX4W
181 lines
6.5 KiB
TypeScript
181 lines
6.5 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
||
import type { DocumentBrief, FileOut } from '../../types/api';
|
||
import { buildDocumentRows, rowBadge } from './documentRows';
|
||
|
||
const doc = (over: Partial<DocumentBrief> = {}): 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> = {}): 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<NonNullable<FileOut['job']>> = {}): 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();
|
||
});
|
||
});
|