phase-7: code restructure

This commit is contained in:
Kazuma
2026-06-05 22:05:55 -04:00
committed by Kazuma
parent 2b785aa861
commit 5b32bd7a04
108 changed files with 8931 additions and 3434 deletions
+149
View File
@@ -0,0 +1,149 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { DataMapper } from '../server/services/DataMapper';
const base = {
price: { quoteType: 'EQUITY', regularMarketPrice: 150 },
assetProfile: { sector: 'Technology', industry: 'Software', category: '' },
financialData: {
quickRatio: 1.2,
debtToEquity: 150,
freeCashflow: 5e9,
revenueGrowth: 0.15,
profitMargins: 0.25,
operatingMargins: 0.3,
returnOnEquity: 0.2,
earningsGrowth: 0.12,
operatingCashflow: 8e9,
},
defaultKeyStatistics: { pegRatio: null, forwardPE: 28, sharesOutstanding: 1e9, priceToBook: 12 },
summaryDetail: {
trailingAnnualDividendYield: 0.005,
trailingPE: 30,
beta: 1.2,
fiftyTwoWeekHigh: 200,
fiftyTwoWeekLow: 120,
},
};
test('maps EQUITY quote type to STOCK', () => {
const result = DataMapper.mapToStandardFormat('AAPL', base);
assert.equal(result.type, 'STOCK');
assert.equal(result.ticker, 'AAPL');
});
test('computes PEG from trailingPE / earningsGrowth when Yahoo returns null', () => {
const result = DataMapper.mapToStandardFormat('AAPL', base);
const expected = +(30 / (0.12 * 100)).toFixed(2); // trailingPE=30, earningsGrowth=12%
assert.equal(result.pegRatio, expected);
});
test('uses Yahoo pegRatio when available', () => {
const summary = {
...base,
defaultKeyStatistics: { ...base.defaultKeyStatistics, pegRatio: 1.5 },
};
const result = DataMapper.mapToStandardFormat('AAPL', summary);
assert.equal(result.pegRatio, 1.5);
});
test('debtToEquity is divided by 100', () => {
const result = DataMapper.mapToStandardFormat('AAPL', base);
assert.equal(result.debtToEquity, 1.5); // 150 / 100
});
test('maps ETF quoteType to ETF', () => {
const etfSummary = {
...base,
price: { ...base.price, quoteType: 'ETF' },
assetProfile: { category: 'Large Blend' },
};
const result = DataMapper.mapToStandardFormat('VOO', etfSummary);
assert.equal(result.type, 'ETF');
});
test('classifies bond ETF from category keyword', () => {
const bondSummary = {
...base,
price: { ...base.price, quoteType: 'ETF' },
assetProfile: { category: 'Intermediate-Term Bond' },
};
const result = DataMapper.mapToStandardFormat('BND', bondSummary);
assert.equal(result.type, 'BOND');
});
test('FCF yield is computed when data available', () => {
const result = DataMapper.mapToStandardFormat('AAPL', base);
assert.notEqual(result.fcfYield, null);
assert((result.fcfYield as number) > 0);
});
test('peRatio prefers trailingPE over forwardPE', () => {
// trailingPE=30 in summaryDetail, forwardPE=28 in defaultKeyStatistics
const result = DataMapper.mapToStandardFormat('AAPL', base);
assert.equal(result.peRatio, 30); // trailing should win
});
test('negative FCF yield is preserved, not nulled', () => {
const negativeFcf = {
...base,
financialData: { ...base.financialData, freeCashflow: -2e9 },
};
const result = DataMapper.mapToStandardFormat('AAPL', negativeFcf);
assert.notEqual(result.fcfYield, null);
assert((result.fcfYield as number) < 0, 'negative FCF should produce negative yield, not null');
});
test('ETF maps volume from summaryDetail', () => {
const etfSummary = {
...base,
price: { ...base.price, quoteType: 'ETF' },
assetProfile: { category: 'Large Blend' },
summaryDetail: {
...base.summaryDetail,
averageVolume: 5000000,
expenseRatio: 0.0003,
trailingAnnualDividendYield: 0.013,
},
defaultKeyStatistics: { fiveYearAverageReturn: 0.12 },
};
const result = DataMapper.mapToStandardFormat('VOO', etfSummary);
assert.equal(result.volume, 5000000);
});
test('bond duration inferred from category — intermediate maps to 5y', () => {
const bondSummary = {
...base,
price: { ...base.price, quoteType: 'ETF' },
assetProfile: { category: 'Intermediate-Term Bond' },
summaryDetail: { yield: 0.045 },
defaultKeyStatistics: {},
};
const result = DataMapper.mapToStandardFormat('BND', bondSummary);
assert.equal(result.duration, 5);
});
test('bond duration inferred from category — short-term maps to 2y', () => {
const bondSummary = {
...base,
price: { ...base.price, quoteType: 'ETF' },
assetProfile: { category: 'Short-Term Bond' },
summaryDetail: { yield: 0.05 },
defaultKeyStatistics: {},
};
const result = DataMapper.mapToStandardFormat('SHY', bondSummary);
assert.equal(result.duration, 2);
});
test('metrics are null (not 0) when data missing', () => {
const sparse = {
price: { quoteType: 'EQUITY', regularMarketPrice: 100 },
financialData: {},
defaultKeyStatistics: {},
summaryDetail: {},
assetProfile: {},
};
const result = DataMapper.mapToStandardFormat('X', sparse);
assert.equal(result.pegRatio, null);
assert.equal(result.quickRatio, null);
});