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); });