@accounter/client
Version:
Accounter client application
139 lines (119 loc) • 5.49 kB
text/typescript
import { describe, expect, it } from 'vitest';
import { buildReportTree, REPORT_ROOT } from '../utils/report-tree.js';
import type { DynamicReportQuery } from '../../../../gql/graphql.js';
// ── fixture helpers ───────────────────────────────────────────────────────────
type TemplateNode = Parameters<typeof buildReportTree>[0][number];
type BusinessSum = Extract<
NonNullable<DynamicReportQuery['businessTransactionsSumFromLedgerRecords']>,
{ __typename?: 'BusinessTransactionsSumFromLedgerRecordsSuccessfulResult' }
>['businessTransactionsSum'][number];
function tmplBranch(
id: string | number,
parent: string | number,
nodeType = 'synthetic-branch',
): TemplateNode {
return { id, parent, text: String(id), droppable: true, data: { nodeType, isOpen: false } };
}
function tmplLeaf(id: string, parent: string | number): TemplateNode {
return {
id,
parent,
text: id,
droppable: false,
data: { nodeType: 'financial-entity', isOpen: false },
};
}
function bizSum(id: string, name: string, totalRaw: number): BusinessSum {
return {
business: {
__typename: 'LtdFinancialEntity',
id,
name,
sortCode: null,
},
credit: { __typename: 'FinancialAmount', formatted: '', raw: 0 },
debit: { __typename: 'FinancialAmount', formatted: '', raw: 0 },
total: { __typename: 'FinancialAmount', formatted: '', raw: totalRaw },
} as BusinessSum;
}
// ── tests ─────────────────────────────────────────────────────────────────────
describe('buildReportTree', () => {
it('empty template → empty reportTree and empty placedEntityIds', () => {
const { reportTree, placedEntityIds } = buildReportTree([], []);
expect(reportTree).toHaveLength(0);
expect(placedEntityIds.size).toBe(0);
});
describe('1 synthetic branch + 2 entity leaves both in businessSums', () => {
const template: TemplateNode[] = [
tmplBranch('br-1', 'report'),
tmplLeaf('e-1', 'br-1'),
tmplLeaf('e-2', 'br-1'),
];
const sums = [bizSum('e-1', 'Alpha Corp', 100), bizSum('e-2', 'Beta Ltd', -50)];
const { reportTree, placedEntityIds } = buildReportTree(template, sums);
it('reportTree has 3 nodes', () => {
expect(reportTree).toHaveLength(3);
});
it('entity leaves are hydrated with name from businessSums', () => {
expect(reportTree.find(n => n.id === 'e-1')!.text).toBe('Alpha Corp');
expect(reportTree.find(n => n.id === 'e-2')!.text).toBe('Beta Ltd');
});
it('entity leaves have value = total.raw * -1', () => {
expect(reportTree.find(n => n.id === 'e-1')!.data.value).toBe(-100);
expect(reportTree.find(n => n.id === 'e-2')!.data.value).toBe(50);
});
it('placedEntityIds contains both entity ids', () => {
expect(placedEntityIds.has('e-1')).toBe(true);
expect(placedEntityIds.has('e-2')).toBe(true);
});
});
describe('entity in template NOT in businessSums', () => {
const template: TemplateNode[] = [
tmplBranch('br-1', 'report'),
tmplLeaf('e-exists', 'br-1'),
tmplLeaf('e-missing', 'br-1'),
];
const sums = [bizSum('e-exists', 'Surviving Entity', 200)];
const { reportTree, placedEntityIds } = buildReportTree(template, sums);
it('missing entity leaf is dropped from reportTree', () => {
expect(reportTree.find(n => n.id === 'e-missing')).toBeUndefined();
});
it('placedEntityIds contains only the surviving entity', () => {
expect(placedEntityIds.has('e-exists')).toBe(true);
expect(placedEntityIds.has('e-missing')).toBe(false);
});
});
describe('sort-code-branch node with numeric id', () => {
it('data.sortCode is set to Number(id)', () => {
const template: TemplateNode[] = [tmplBranch(100, 'report', 'sort-code-branch')];
const { reportTree } = buildReportTree(template, []);
const branch = reportTree.find(n => n.id === '100');
expect(branch).toBeDefined();
expect(branch!.data.sortCode).toBe(100);
});
});
describe('parent: 0 is replaced with REPORT_ROOT', () => {
it('branch node with parent 0 gets parent = REPORT_ROOT', () => {
const template: TemplateNode[] = [tmplBranch('br-1', 0)];
const { reportTree } = buildReportTree(template, []);
expect(reportTree.find(n => n.id === 'br-1')!.parent).toBe(REPORT_ROOT);
});
it('leaf node with parent 0 gets parent = REPORT_ROOT', () => {
const template: TemplateNode[] = [tmplLeaf('e-1', 0)];
const { reportTree } = buildReportTree(template, [bizSum('e-1', 'Root Entity', 10)]);
expect(reportTree.find(n => n.id === 'e-1')!.parent).toBe(REPORT_ROOT);
});
});
describe('nested branches: parent IDs preserved exactly', () => {
it('child branch parent points to parent branch id', () => {
const template: TemplateNode[] = [
tmplBranch('outer', 'report'),
tmplBranch('inner', 'outer'),
tmplLeaf('e-1', 'inner'),
];
const { reportTree } = buildReportTree(template, [bizSum('e-1', 'Nested Entity', 30)]);
expect(reportTree.find(n => n.id === 'inner')!.parent).toBe('outer');
expect(reportTree.find(n => n.id === 'e-1')!.parent).toBe('inner');
});
});
});