Fix financial taxonomy snapshot normalization
This commit is contained in:
185
lib/financials/page-merge.test.ts
Normal file
185
lib/financials/page-merge.test.ts
Normal file
@@ -0,0 +1,185 @@
|
||||
import { describe, expect, it } from 'bun:test';
|
||||
import { __financialPageMergeInternals } from './page-merge';
|
||||
import type { CompanyFinancialStatementsResponse } from '@/lib/types';
|
||||
|
||||
function createResponse(partial: Partial<CompanyFinancialStatementsResponse>): CompanyFinancialStatementsResponse {
|
||||
return {
|
||||
company: {
|
||||
ticker: 'MSFT',
|
||||
companyName: 'Microsoft Corporation',
|
||||
cik: null
|
||||
},
|
||||
surfaceKind: 'income_statement',
|
||||
cadence: 'annual',
|
||||
displayModes: ['standardized', 'faithful'],
|
||||
defaultDisplayMode: 'standardized',
|
||||
periods: [],
|
||||
statementRows: {
|
||||
faithful: [],
|
||||
standardized: []
|
||||
},
|
||||
statementDetails: null,
|
||||
ratioRows: null,
|
||||
kpiRows: null,
|
||||
trendSeries: [],
|
||||
categories: [],
|
||||
availability: {
|
||||
adjusted: false,
|
||||
customMetrics: false
|
||||
},
|
||||
nextCursor: null,
|
||||
facts: null,
|
||||
coverage: {
|
||||
filings: 0,
|
||||
rows: 0,
|
||||
dimensions: 0,
|
||||
facts: 0
|
||||
},
|
||||
dataSourceStatus: {
|
||||
enabled: true,
|
||||
hydratedFilings: 0,
|
||||
partialFilings: 0,
|
||||
failedFilings: 0,
|
||||
pendingFilings: 0,
|
||||
queuedSync: false
|
||||
},
|
||||
metrics: {
|
||||
taxonomy: null,
|
||||
validation: null
|
||||
},
|
||||
normalization: {
|
||||
parserEngine: 'fiscal-xbrl',
|
||||
regime: 'us-gaap',
|
||||
fiscalPack: 'core',
|
||||
parserVersion: '0.1.0',
|
||||
surfaceRowCount: 0,
|
||||
detailRowCount: 0,
|
||||
kpiRowCount: 0,
|
||||
unmappedRowCount: 0,
|
||||
materialUnmappedRowCount: 0,
|
||||
warnings: []
|
||||
},
|
||||
dimensionBreakdown: null,
|
||||
...partial
|
||||
};
|
||||
}
|
||||
|
||||
describe('financial page merge helpers', () => {
|
||||
it('merges detail maps safely when legacy detail rows are missing arrays', () => {
|
||||
const merged = __financialPageMergeInternals.mergeDetailMaps(
|
||||
{
|
||||
revenue: [{
|
||||
key: 'detail',
|
||||
parentSurfaceKey: 'revenue',
|
||||
label: 'Detail',
|
||||
conceptKey: 'detail',
|
||||
qname: 'us-gaap:Detail',
|
||||
namespaceUri: 'http://fasb.org/us-gaap/2024',
|
||||
localName: 'Detail',
|
||||
unit: 'iso4217:USD',
|
||||
values: { p1: 1 },
|
||||
sourceFactIds: undefined,
|
||||
isExtension: false,
|
||||
dimensionsSummary: undefined,
|
||||
residualFlag: false
|
||||
} as never]
|
||||
},
|
||||
{
|
||||
revenue: [{
|
||||
key: 'detail',
|
||||
parentSurfaceKey: 'revenue',
|
||||
label: 'Detail',
|
||||
conceptKey: 'detail',
|
||||
qname: 'us-gaap:Detail',
|
||||
namespaceUri: 'http://fasb.org/us-gaap/2024',
|
||||
localName: 'Detail',
|
||||
unit: 'iso4217:USD',
|
||||
values: { p2: 2 },
|
||||
sourceFactIds: [2],
|
||||
isExtension: false,
|
||||
dimensionsSummary: ['region:americas'],
|
||||
residualFlag: false
|
||||
}]
|
||||
}
|
||||
);
|
||||
|
||||
expect(merged?.revenue?.[0]).toMatchObject({
|
||||
values: { p1: 1, p2: 2 },
|
||||
sourceFactIds: [2],
|
||||
dimensionsSummary: ['region:americas']
|
||||
});
|
||||
});
|
||||
|
||||
it('merges paged financial responses safely when row arrays are partially missing', () => {
|
||||
const base = createResponse({
|
||||
periods: [{
|
||||
id: 'p1',
|
||||
filingId: 1,
|
||||
accessionNumber: '0001',
|
||||
filingDate: '2025-01-01',
|
||||
periodStart: '2024-01-01',
|
||||
periodEnd: '2024-12-31',
|
||||
filingType: '10-K',
|
||||
periodLabel: 'FY 2024'
|
||||
}],
|
||||
statementRows: {
|
||||
faithful: undefined as never,
|
||||
standardized: [{
|
||||
key: 'revenue',
|
||||
label: 'Revenue',
|
||||
category: 'revenue',
|
||||
order: 10,
|
||||
unit: 'currency',
|
||||
values: { p1: 1 },
|
||||
sourceConcepts: [],
|
||||
sourceRowKeys: [],
|
||||
sourceFactIds: [],
|
||||
formulaKey: null,
|
||||
hasDimensions: false,
|
||||
resolvedSourceRowKeys: { p1: 'revenue' }
|
||||
}]
|
||||
}
|
||||
});
|
||||
const next = createResponse({
|
||||
periods: [{
|
||||
id: 'p2',
|
||||
filingId: 2,
|
||||
accessionNumber: '0002',
|
||||
filingDate: '2026-01-01',
|
||||
periodStart: '2025-01-01',
|
||||
periodEnd: '2025-12-31',
|
||||
filingType: '10-K',
|
||||
periodLabel: 'FY 2025'
|
||||
}],
|
||||
statementRows: {
|
||||
faithful: [{
|
||||
key: 'rev',
|
||||
label: 'Revenue',
|
||||
conceptKey: 'rev',
|
||||
qname: 'us-gaap:Revenue',
|
||||
namespaceUri: 'http://fasb.org/us-gaap/2024',
|
||||
localName: 'Revenue',
|
||||
isExtension: false,
|
||||
statement: 'income',
|
||||
roleUri: 'income',
|
||||
order: 10,
|
||||
depth: 0,
|
||||
parentKey: null,
|
||||
values: { p2: 2 },
|
||||
units: { p2: 'iso4217:USD' },
|
||||
hasDimensions: false,
|
||||
sourceFactIds: []
|
||||
}],
|
||||
standardized: undefined as never
|
||||
}
|
||||
});
|
||||
|
||||
const merged = __financialPageMergeInternals.mergeFinancialPages(base, next);
|
||||
|
||||
expect(merged.periods.map((period) => period.id)).toEqual(['p1', 'p2']);
|
||||
expect(merged.statementRows).toMatchObject({
|
||||
faithful: [{ key: 'rev' }],
|
||||
standardized: [{ key: 'revenue', values: { p1: 1 } }]
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user