Files
Neon-Desk/lib/server/financial-taxonomy.test.ts

366 lines
11 KiB
TypeScript

import { describe, expect, it } from 'bun:test';
import { __financialTaxonomyInternals } from './financial-taxonomy';
import type { FilingTaxonomySnapshotRecord } from './repos/filing-taxonomy';
import type {
FinancialStatementKind,
FinancialStatementPeriod,
TaxonomyFactRow,
TaxonomyStatementRow
} from '@/lib/types';
function createRow(input: {
key?: string;
label?: string;
conceptKey?: string;
qname?: string;
localName?: string;
statement?: FinancialStatementKind;
order?: number;
depth?: number;
hasDimensions?: boolean;
values: Record<string, number | null>;
sourceFactIds?: number[];
}): TaxonomyStatementRow {
const localName = input.localName ?? 'RevenueFromContractWithCustomerExcludingAssessedTax';
const conceptKey = input.conceptKey ?? `http://fasb.org/us-gaap/2024#${localName}`;
const qname = input.qname ?? `us-gaap:${localName}`;
return {
key: input.key ?? conceptKey,
label: input.label ?? localName,
conceptKey,
qname,
namespaceUri: 'http://fasb.org/us-gaap/2024',
localName,
isExtension: false,
statement: input.statement ?? 'income',
roleUri: input.statement ?? 'income',
order: input.order ?? 1,
depth: input.depth ?? 0,
parentKey: null,
values: input.values,
units: Object.fromEntries(Object.keys(input.values).map((periodId) => [periodId, 'iso4217:USD'])),
hasDimensions: input.hasDimensions ?? false,
sourceFactIds: input.sourceFactIds ?? [1]
};
}
function createSnapshot(input: {
filingId: number;
filingType: '10-K' | '10-Q';
filingDate: string;
periods: Array<{
id: string;
periodStart: string | null;
periodEnd: string;
periodLabel: string;
}>;
statement: FinancialStatementKind;
rows?: TaxonomyStatementRow[];
}) {
const defaultRow = createRow({
statement: input.statement,
values: Object.fromEntries(input.periods.map((period, index) => [period.id, 100 + index]))
});
return {
id: input.filingId,
filing_id: input.filingId,
ticker: 'MSFT',
filing_date: input.filingDate,
filing_type: input.filingType,
parse_status: 'ready',
parse_error: null,
source: 'xbrl_instance',
periods: input.periods.map((period) => ({
id: period.id,
filingId: input.filingId,
accessionNumber: `0000-${input.filingId}`,
filingDate: input.filingDate,
periodStart: period.periodStart,
periodEnd: period.periodEnd,
filingType: input.filingType,
periodLabel: period.periodLabel
})),
statement_rows: {
income: input.statement === 'income' ? (input.rows ?? [defaultRow]) : [],
balance: input.statement === 'balance' ? (input.rows ?? [{ ...defaultRow, statement: 'balance' }]) : [],
cash_flow: input.statement === 'cash_flow' ? (input.rows ?? [{ ...defaultRow, statement: 'cash_flow' }]) : [],
equity: [],
comprehensive_income: []
},
derived_metrics: null,
validation_result: null,
facts_count: 0,
concepts_count: 0,
dimensions_count: 0,
created_at: input.filingDate,
updated_at: input.filingDate
} satisfies FilingTaxonomySnapshotRecord;
}
function createPeriod(input: {
id: string;
filingId: number;
filingDate: string;
periodEnd: string;
periodStart?: string | null;
filingType?: '10-K' | '10-Q';
}): FinancialStatementPeriod {
return {
id: input.id,
filingId: input.filingId,
accessionNumber: `0000-${input.filingId}`,
filingDate: input.filingDate,
periodStart: input.periodStart ?? null,
periodEnd: input.periodEnd,
filingType: input.filingType ?? '10-Q',
periodLabel: 'Test period'
};
}
function createDimensionFact(input: {
filingId: number;
filingDate: string;
conceptKey: string;
qname: string;
localName: string;
periodEnd: string;
value: number;
axis?: string;
member?: string;
}): TaxonomyFactRow {
return {
id: input.filingId,
snapshotId: input.filingId,
filingId: input.filingId,
filingDate: input.filingDate,
statement: 'income',
roleUri: 'income',
conceptKey: input.conceptKey,
qname: input.qname,
namespaceUri: 'http://fasb.org/us-gaap/2024',
localName: input.localName,
value: input.value,
contextId: `ctx-${input.filingId}`,
unit: 'iso4217:USD',
decimals: null,
periodStart: '2025-01-01',
periodEnd: input.periodEnd,
periodInstant: null,
dimensions: [{
axis: input.axis ?? 'srt:ProductOrServiceAxis',
member: input.member ?? 'msft:CloudMember'
}],
isDimensionless: false,
sourceFile: null
};
}
describe('financial taxonomy internals', () => {
it('selects the primary quarter duration for 10-Q income statements', () => {
const snapshot = createSnapshot({
filingId: 1,
filingType: '10-Q',
filingDate: '2026-01-28',
statement: 'income',
periods: [
{ id: 'instant', periodStart: null, periodEnd: '2025-12-31', periodLabel: 'Instant' },
{ id: 'quarter', periodStart: '2025-10-01', periodEnd: '2025-12-31', periodLabel: '2025-10-01 to 2025-12-31' },
{ id: 'ytd', periodStart: '2025-07-01', periodEnd: '2025-12-31', periodLabel: '2025-07-01 to 2025-12-31' }
]
});
const selection = __financialTaxonomyInternals.selectPrimaryPeriods([snapshot], 'income');
expect(selection.periods).toHaveLength(1);
expect(selection.periods[0]?.id).toBe('quarter');
});
it('selects the latest instant for balance sheets', () => {
const snapshot = createSnapshot({
filingId: 2,
filingType: '10-K',
filingDate: '2025-07-30',
statement: 'balance',
periods: [
{ id: 'prior', periodStart: null, periodEnd: '2024-06-30', periodLabel: 'Instant' },
{ id: 'current', periodStart: null, periodEnd: '2025-06-30', periodLabel: 'Instant' }
]
});
const selection = __financialTaxonomyInternals.selectPrimaryPeriods([snapshot], 'balance');
expect(selection.periods).toHaveLength(1);
expect(selection.periods[0]?.id).toBe('current');
});
it('builds one reporting period per filing for the selected statement', () => {
const annual = createSnapshot({
filingId: 10,
filingType: '10-K',
filingDate: '2025-07-30',
statement: 'income',
periods: [
{ id: 'annual', periodStart: '2024-07-01', periodEnd: '2025-06-30', periodLabel: '2024-07-01 to 2025-06-30' },
{ id: 'quarter', periodStart: '2025-04-01', periodEnd: '2025-06-30', periodLabel: '2025-04-01 to 2025-06-30' }
]
});
const quarterly = createSnapshot({
filingId: 11,
filingType: '10-Q',
filingDate: '2025-10-29',
statement: 'income',
periods: [
{ id: 'instant', periodStart: null, periodEnd: '2025-09-30', periodLabel: 'Instant' },
{ id: 'quarter', periodStart: '2025-07-01', periodEnd: '2025-09-30', periodLabel: '2025-07-01 to 2025-09-30' },
{ id: 'ytd', periodStart: '2025-01-01', periodEnd: '2025-09-30', periodLabel: '2025-01-01 to 2025-09-30' }
]
});
const periods = __financialTaxonomyInternals.buildPeriods([annual, quarterly], 'income');
expect(periods.map((period) => period.id)).toEqual(['annual', 'quarter']);
});
it('maps overlapping GAAP aliases into one standardized COGS row while preserving faithful rows', () => {
const period2024 = createPeriod({
id: '2024-q4',
filingId: 30,
filingDate: '2025-01-29',
periodEnd: '2024-12-31'
});
const period2025 = createPeriod({
id: '2025-q4',
filingId: 31,
filingDate: '2026-01-28',
periodEnd: '2025-12-31'
});
const faithfulRows = __financialTaxonomyInternals.buildRows([
createSnapshot({
filingId: 30,
filingType: '10-Q',
filingDate: '2025-01-29',
statement: 'income',
periods: [{
id: '2024-q4',
periodStart: '2024-10-01',
periodEnd: '2024-12-31',
periodLabel: '2024-10-01 to 2024-12-31'
}],
rows: [
createRow({
localName: 'CostOfRevenue',
label: 'Cost of Revenue',
values: { '2024-q4': 45_000 },
sourceFactIds: [101]
})
]
}),
createSnapshot({
filingId: 31,
filingType: '10-Q',
filingDate: '2026-01-28',
statement: 'income',
periods: [{
id: '2025-q4',
periodStart: '2025-10-01',
periodEnd: '2025-12-31',
periodLabel: '2025-10-01 to 2025-12-31'
}],
rows: [
createRow({
localName: 'CostOfGoodsSold',
label: 'Cost of Goods Sold',
values: { '2025-q4': 48_000 },
sourceFactIds: [202]
})
]
})
], 'income', new Set(['2024-q4', '2025-q4']));
const standardizedRows = __financialTaxonomyInternals.buildStandardizedRows(
faithfulRows,
'income',
[period2024, period2025]
);
expect(faithfulRows).toHaveLength(2);
const cogs = standardizedRows.find((row) => row.key === 'cost-of-revenue');
expect(cogs).toBeDefined();
expect(cogs?.values['2024-q4']).toBe(45_000);
expect(cogs?.values['2025-q4']).toBe(48_000);
expect(cogs?.sourceConcepts).toEqual([
'us-gaap:CostOfGoodsSold',
'us-gaap:CostOfRevenue'
]);
expect(cogs?.sourceRowKeys).toHaveLength(2);
});
it('aggregates standardized dimension drill-down across mapped source concepts', () => {
const period2024 = createPeriod({
id: '2024-q4',
filingId: 40,
filingDate: '2025-01-29',
periodEnd: '2024-12-31'
});
const period2025 = createPeriod({
id: '2025-q4',
filingId: 41,
filingDate: '2026-01-28',
periodEnd: '2025-12-31'
});
const faithfulRows = [
createRow({
localName: 'CostOfRevenue',
label: 'Cost of Revenue',
values: { '2024-q4': 45_000 },
hasDimensions: true
}),
createRow({
localName: 'CostOfGoodsSold',
label: 'Cost of Goods Sold',
values: { '2025-q4': 48_000 },
hasDimensions: true
})
];
const standardizedRows = __financialTaxonomyInternals.buildStandardizedRows(
faithfulRows,
'income',
[period2024, period2025]
);
const breakdown = __financialTaxonomyInternals.buildDimensionBreakdown([
createDimensionFact({
filingId: 40,
filingDate: '2025-01-29',
conceptKey: faithfulRows[0].key,
qname: faithfulRows[0].qname,
localName: faithfulRows[0].localName,
periodEnd: '2024-12-31',
value: 20_000,
member: 'msft:ProductivityMember'
}),
createDimensionFact({
filingId: 41,
filingDate: '2026-01-28',
conceptKey: faithfulRows[1].key,
qname: faithfulRows[1].qname,
localName: faithfulRows[1].localName,
periodEnd: '2025-12-31',
value: 28_000,
member: 'msft:IntelligentCloudMember'
})
], [period2024, period2025], faithfulRows, standardizedRows);
const cogs = breakdown?.['cost-of-revenue'] ?? [];
expect(cogs).toHaveLength(2);
expect(cogs.map((row) => row.sourceLabel)).toEqual([
'Cost of Revenue',
'Cost of Goods Sold'
]);
});
});