Fix financial taxonomy snapshot normalization
This commit is contained in:
291
lib/server/repos/filing-taxonomy.test.ts
Normal file
291
lib/server/repos/filing-taxonomy.test.ts
Normal file
@@ -0,0 +1,291 @@
|
||||
import { describe, expect, it } from 'bun:test';
|
||||
import { __filingTaxonomyInternals } from './filing-taxonomy';
|
||||
|
||||
describe('filing taxonomy snapshot normalization', () => {
|
||||
it('normalizes legacy snake_case nested snapshot payloads in toSnapshotRecord', () => {
|
||||
const record = __filingTaxonomyInternals.toSnapshotRecord({
|
||||
id: 1,
|
||||
filing_id: 10,
|
||||
ticker: 'MSFT',
|
||||
filing_date: '2026-01-28',
|
||||
filing_type: '10-Q',
|
||||
parse_status: 'ready',
|
||||
parse_error: null,
|
||||
source: 'xbrl_instance',
|
||||
parser_engine: 'fiscal-xbrl',
|
||||
parser_version: '0.1.0',
|
||||
taxonomy_regime: 'us-gaap',
|
||||
fiscal_pack: 'core',
|
||||
periods: [{
|
||||
id: 'fy-2025',
|
||||
filing_id: 10,
|
||||
accession_number: '0001',
|
||||
filing_date: '2026-01-28',
|
||||
period_start: '2025-01-01',
|
||||
period_end: '2025-12-31',
|
||||
filing_type: '10-Q',
|
||||
period_label: 'FY 2025'
|
||||
}],
|
||||
faithful_rows: {
|
||||
income: [{
|
||||
key: 'revenue',
|
||||
label: 'Revenue',
|
||||
concept_key: 'us-gaap:Revenue',
|
||||
qname: 'us-gaap:Revenue',
|
||||
namespace_uri: 'http://fasb.org/us-gaap/2025',
|
||||
local_name: 'Revenue',
|
||||
is_extension: false,
|
||||
statement: 'income',
|
||||
role_uri: 'income',
|
||||
order: 10,
|
||||
depth: 0,
|
||||
parent_key: null,
|
||||
values: { 'fy-2025': 10 },
|
||||
units: { 'fy-2025': 'iso4217:USD' },
|
||||
has_dimensions: false,
|
||||
source_fact_ids: [1]
|
||||
}],
|
||||
balance: [],
|
||||
cash_flow: [],
|
||||
equity: [],
|
||||
comprehensive_income: []
|
||||
},
|
||||
statement_rows: null,
|
||||
surface_rows: {
|
||||
income: [{
|
||||
key: 'revenue',
|
||||
label: 'Revenue',
|
||||
category: 'revenue',
|
||||
template_section: 'revenue',
|
||||
order: 10,
|
||||
unit: 'currency',
|
||||
values: { 'fy-2025': 10 },
|
||||
source_concepts: ['us-gaap:Revenue'],
|
||||
source_row_keys: ['revenue'],
|
||||
source_fact_ids: [1],
|
||||
formula_key: null,
|
||||
has_dimensions: false,
|
||||
resolved_source_row_keys: { 'fy-2025': 'revenue' },
|
||||
statement: 'income',
|
||||
detail_count: 1,
|
||||
resolution_method: 'direct',
|
||||
confidence: 'high',
|
||||
warning_codes: ['legacy_surface']
|
||||
}],
|
||||
balance: [],
|
||||
cash_flow: [],
|
||||
equity: [],
|
||||
comprehensive_income: []
|
||||
},
|
||||
detail_rows: {
|
||||
income: {
|
||||
revenue: [{
|
||||
key: 'revenue_detail',
|
||||
parent_surface_key: 'revenue',
|
||||
label: 'Revenue Detail',
|
||||
concept_key: 'us-gaap:RevenueDetail',
|
||||
qname: 'us-gaap:RevenueDetail',
|
||||
namespace_uri: 'http://fasb.org/us-gaap/2025',
|
||||
local_name: 'RevenueDetail',
|
||||
unit: 'iso4217:USD',
|
||||
values: { 'fy-2025': 10 },
|
||||
source_fact_ids: [2],
|
||||
is_extension: false,
|
||||
dimensions_summary: ['region:americas'],
|
||||
residual_flag: false
|
||||
}]
|
||||
},
|
||||
balance: {},
|
||||
cash_flow: {},
|
||||
equity: {},
|
||||
comprehensive_income: {}
|
||||
},
|
||||
kpi_rows: [{
|
||||
key: 'cloud_growth',
|
||||
label: 'Cloud Growth',
|
||||
category: 'operating_kpi',
|
||||
unit: 'percent',
|
||||
order: 10,
|
||||
segment: null,
|
||||
axis: null,
|
||||
member: null,
|
||||
values: { 'fy-2025': 0.25 },
|
||||
source_concepts: ['msft:CloudGrowth'],
|
||||
source_fact_ids: [3],
|
||||
provenance_type: 'taxonomy',
|
||||
has_dimensions: false
|
||||
}],
|
||||
derived_metrics: null,
|
||||
validation_result: null,
|
||||
normalization_summary: {
|
||||
surface_row_count: 1,
|
||||
detail_row_count: 1,
|
||||
kpi_row_count: 1,
|
||||
unmapped_row_count: 0,
|
||||
material_unmapped_row_count: 0,
|
||||
warnings: ['legacy_warning']
|
||||
},
|
||||
facts_count: 3,
|
||||
concepts_count: 3,
|
||||
dimensions_count: 1,
|
||||
created_at: '2026-01-28T00:00:00.000Z',
|
||||
updated_at: '2026-01-28T00:00:00.000Z'
|
||||
} as never);
|
||||
|
||||
expect(record.periods[0]).toMatchObject({
|
||||
filingId: 10,
|
||||
accessionNumber: '0001',
|
||||
filingDate: '2026-01-28',
|
||||
periodStart: '2025-01-01',
|
||||
periodEnd: '2025-12-31',
|
||||
periodLabel: 'FY 2025'
|
||||
});
|
||||
expect(record.faithful_rows.income[0]).toMatchObject({
|
||||
conceptKey: 'us-gaap:Revenue',
|
||||
namespaceUri: 'http://fasb.org/us-gaap/2025',
|
||||
localName: 'Revenue',
|
||||
roleUri: 'income',
|
||||
parentKey: null,
|
||||
hasDimensions: false,
|
||||
sourceFactIds: [1]
|
||||
});
|
||||
expect(record.surface_rows.income[0]).toMatchObject({
|
||||
templateSection: 'revenue',
|
||||
sourceConcepts: ['us-gaap:Revenue'],
|
||||
sourceRowKeys: ['revenue'],
|
||||
sourceFactIds: [1],
|
||||
formulaKey: null,
|
||||
hasDimensions: false,
|
||||
resolvedSourceRowKeys: { 'fy-2025': 'revenue' },
|
||||
detailCount: 1,
|
||||
resolutionMethod: 'direct',
|
||||
warningCodes: ['legacy_surface']
|
||||
});
|
||||
expect(record.detail_rows.income.revenue?.[0]).toMatchObject({
|
||||
parentSurfaceKey: 'revenue',
|
||||
conceptKey: 'us-gaap:RevenueDetail',
|
||||
namespaceUri: 'http://fasb.org/us-gaap/2025',
|
||||
sourceFactIds: [2],
|
||||
dimensionsSummary: ['region:americas'],
|
||||
residualFlag: false
|
||||
});
|
||||
expect(record.kpi_rows[0]).toMatchObject({
|
||||
sourceConcepts: ['msft:CloudGrowth'],
|
||||
sourceFactIds: [3],
|
||||
provenanceType: 'taxonomy',
|
||||
hasDimensions: false
|
||||
});
|
||||
expect(record.normalization_summary).toEqual({
|
||||
surfaceRowCount: 1,
|
||||
detailRowCount: 1,
|
||||
kpiRowCount: 1,
|
||||
unmappedRowCount: 0,
|
||||
materialUnmappedRowCount: 0,
|
||||
warnings: ['legacy_warning']
|
||||
});
|
||||
});
|
||||
|
||||
it('keeps mixed camelCase and snake_case payloads compatible', () => {
|
||||
const normalized = __filingTaxonomyInternals.normalizeFilingTaxonomySnapshotPayload({
|
||||
periods: [{
|
||||
id: 'fy-2025',
|
||||
filingId: 10,
|
||||
accessionNumber: '0001',
|
||||
filingDate: '2026-01-28',
|
||||
periodStart: '2025-01-01',
|
||||
periodEnd: '2025-12-31',
|
||||
filingType: '10-K',
|
||||
periodLabel: 'FY 2025'
|
||||
}],
|
||||
faithful_rows: {
|
||||
income: [{
|
||||
key: 'revenue',
|
||||
label: 'Revenue',
|
||||
conceptKey: 'us-gaap:Revenue',
|
||||
qname: 'us-gaap:Revenue',
|
||||
namespaceUri: 'http://fasb.org/us-gaap/2025',
|
||||
localName: 'Revenue',
|
||||
isExtension: false,
|
||||
statement: 'income',
|
||||
roleUri: 'income',
|
||||
order: 10,
|
||||
depth: 0,
|
||||
parentKey: null,
|
||||
values: { 'fy-2025': 10 },
|
||||
units: { 'fy-2025': 'iso4217:USD' },
|
||||
hasDimensions: false,
|
||||
sourceFactIds: [1]
|
||||
}],
|
||||
balance: [],
|
||||
cash_flow: [],
|
||||
equity: [],
|
||||
comprehensive_income: []
|
||||
},
|
||||
statement_rows: null,
|
||||
surface_rows: {
|
||||
income: [{
|
||||
key: 'revenue',
|
||||
label: 'Revenue',
|
||||
category: 'revenue',
|
||||
order: 10,
|
||||
unit: 'currency',
|
||||
values: { 'fy-2025': 10 },
|
||||
source_concepts: ['us-gaap:Revenue'],
|
||||
source_row_keys: ['revenue'],
|
||||
source_fact_ids: [1],
|
||||
formula_key: null,
|
||||
has_dimensions: false,
|
||||
resolved_source_row_keys: { 'fy-2025': 'revenue' }
|
||||
}],
|
||||
balance: [],
|
||||
cash_flow: [],
|
||||
equity: [],
|
||||
comprehensive_income: []
|
||||
},
|
||||
detail_rows: {
|
||||
income: {
|
||||
revenue: [{
|
||||
key: 'revenue_detail',
|
||||
parentSurfaceKey: 'revenue',
|
||||
label: 'Revenue Detail',
|
||||
conceptKey: 'us-gaap:RevenueDetail',
|
||||
qname: 'us-gaap:RevenueDetail',
|
||||
namespaceUri: 'http://fasb.org/us-gaap/2025',
|
||||
localName: 'RevenueDetail',
|
||||
unit: 'iso4217:USD',
|
||||
values: { 'fy-2025': 10 },
|
||||
sourceFactIds: [2],
|
||||
isExtension: false,
|
||||
dimensionsSummary: [],
|
||||
residualFlag: false
|
||||
}]
|
||||
},
|
||||
balance: {},
|
||||
cash_flow: {},
|
||||
equity: {},
|
||||
comprehensive_income: {}
|
||||
},
|
||||
kpi_rows: [],
|
||||
normalization_summary: {
|
||||
surfaceRowCount: 1,
|
||||
detail_row_count: 1,
|
||||
kpiRowCount: 0,
|
||||
unmapped_row_count: 0,
|
||||
materialUnmappedRowCount: 0,
|
||||
warnings: []
|
||||
}
|
||||
});
|
||||
|
||||
expect(normalized.periods[0]?.filingId).toBe(10);
|
||||
expect(normalized.surface_rows.income[0]?.sourceConcepts).toEqual(['us-gaap:Revenue']);
|
||||
expect(normalized.detail_rows.income.revenue?.[0]?.parentSurfaceKey).toBe('revenue');
|
||||
expect(normalized.normalization_summary).toEqual({
|
||||
surfaceRowCount: 1,
|
||||
detailRowCount: 1,
|
||||
kpiRowCount: 0,
|
||||
unmappedRowCount: 0,
|
||||
materialUnmappedRowCount: 0,
|
||||
warnings: []
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user