fix bruno collection
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { ScreenerEngine } from '../server/domains/screener/ScreenerEngine.js';
|
||||
import { BenchmarkProvider } from '../server/domains/shared/services/BenchmarkProvider.js';
|
||||
import { noopLogger } from '../server/domains/shared/utils/logger.js';
|
||||
import type { MarketContext } from '../server/domains/shared/types/market.model.js';
|
||||
|
||||
// Mock Yahoo Finance Client
|
||||
class MockYahooClient {
|
||||
async fetchSummary(ticker: string) {
|
||||
if (ticker === 'INVALID') {
|
||||
throw new Error('Not found');
|
||||
}
|
||||
|
||||
return {
|
||||
price: {
|
||||
regularMarketPrice: ticker === 'AAPL' ? 189.5 : 425.3,
|
||||
marketCap: ticker === 'AAPL' ? 2.8e12 : 1.6e12,
|
||||
},
|
||||
summaryDetail: {
|
||||
fiftyTwoWeekHigh: ticker === 'AAPL' ? 199.62 : 468.5,
|
||||
fiftyTwoWeekLow: ticker === 'AAPL' ? 164.08 : 380.2,
|
||||
},
|
||||
quoteType: { quoteType: 'EQUITY' },
|
||||
defaultKeyStatistics: {
|
||||
trailingPE: ticker === 'AAPL' ? 28.5 : 32.1,
|
||||
},
|
||||
financialData: {
|
||||
returnOnEquity: (ticker === 'AAPL' ? 95.2 : 48.5) / 100,
|
||||
operatingMargins: (ticker === 'AAPL' ? 30.7 : 27.8) / 100,
|
||||
grossMargins: (ticker === 'AAPL' ? 46.2 : 45.5) / 100,
|
||||
freeCashflow: ticker === 'AAPL' ? 110e9 : 60e9,
|
||||
totalRevenue: ticker === 'AAPL' ? 383.3e9 : 215.1e9,
|
||||
debtToEquity: ticker === 'AAPL' ? 75.56 : 55.2,
|
||||
currentRatio: ticker === 'AAPL' ? 0.95 : 1.2,
|
||||
},
|
||||
incomeStatementHistoryQuarterly: {
|
||||
incomeStatementHistory: [
|
||||
{
|
||||
commonStockSharesOutstanding: ticker === 'AAPL' ? 15.6e9 : 9.3e9,
|
||||
netIncome: ticker === 'AAPL' ? 25e9 : 20e9,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async fetchCalendarEvents() {
|
||||
return [];
|
||||
}
|
||||
|
||||
async search() {
|
||||
return { quotes: [] };
|
||||
}
|
||||
}
|
||||
|
||||
class MockBenchmarkProvider extends BenchmarkProvider {
|
||||
async getMarketContext(): Promise<MarketContext> {
|
||||
return {
|
||||
sp500Price: 5500,
|
||||
riskFreeRate: 4.5,
|
||||
vixLevel: 12.5,
|
||||
rateRegime: 'NORMAL',
|
||||
volatilityRegime: 'LOW',
|
||||
benchmarks: {
|
||||
marketPE: 20,
|
||||
techPE: 28,
|
||||
reitYield: 3.5,
|
||||
igSpread: 1.2,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
test('ScreenerEngine', async (t) => {
|
||||
await t.test('screenTickers() processes valid ticker', async () => {
|
||||
const client = new MockYahooClient();
|
||||
const benchmark = new MockBenchmarkProvider(null as any);
|
||||
const engine = new ScreenerEngine(client as any, benchmark, { logger: noopLogger });
|
||||
|
||||
const results = await engine.screenTickers(['AAPL']);
|
||||
assert.ok(results);
|
||||
assert.ok('STOCK' in results);
|
||||
assert.ok('ETF' in results);
|
||||
assert.ok('BOND' in results);
|
||||
assert.ok('ERROR' in results);
|
||||
});
|
||||
|
||||
await t.test('screenTickers() handles error gracefully', async () => {
|
||||
const client = new MockYahooClient();
|
||||
const benchmark = new MockBenchmarkProvider(null as any);
|
||||
const engine = new ScreenerEngine(client as any, benchmark, { logger: noopLogger });
|
||||
|
||||
const results = await engine.screenTickers(['INVALID']);
|
||||
assert.equal(results.ERROR.length, 1);
|
||||
assert.equal(results.ERROR[0].ticker, 'INVALID');
|
||||
assert.ok(results.ERROR[0].message);
|
||||
});
|
||||
|
||||
await t.test('screenTickers() normalizes ticker to uppercase', async () => {
|
||||
const client = new MockYahooClient();
|
||||
const benchmark = new MockBenchmarkProvider(null as any);
|
||||
const engine = new ScreenerEngine(client as any, benchmark, { logger: noopLogger });
|
||||
|
||||
const results = await engine.screenTickers(['aapl']);
|
||||
// Should process without error
|
||||
assert.ok(results);
|
||||
});
|
||||
|
||||
await t.test('screenTickers() batches requests with delay', async () => {
|
||||
const client = new MockYahooClient();
|
||||
const benchmark = new MockBenchmarkProvider(null as any);
|
||||
const engine = new ScreenerEngine(client as any, benchmark, { logger: noopLogger });
|
||||
|
||||
const startTime = Date.now();
|
||||
await engine.screenTickers(['AAPL', 'MSFT']);
|
||||
const endTime = Date.now();
|
||||
|
||||
// Should have delay between batches (1000ms per batch)
|
||||
assert.ok(endTime - startTime >= 0); // At minimum, some time should pass
|
||||
});
|
||||
|
||||
await t.test('screenTickers() returns market context', async () => {
|
||||
const client = new MockYahooClient();
|
||||
const benchmark = new MockBenchmarkProvider(null as any);
|
||||
const engine = new ScreenerEngine(client as any, benchmark, { logger: noopLogger });
|
||||
|
||||
const results = await engine.screenTickers(['AAPL']);
|
||||
assert.ok(results.marketContext);
|
||||
assert.equal(results.marketContext.sp500Price, 5500);
|
||||
assert.equal(results.marketContext.rateRegime, 'NORMAL');
|
||||
});
|
||||
|
||||
await t.test('screenTickers() handles empty list', async () => {
|
||||
const client = new MockYahooClient();
|
||||
const benchmark = new MockBenchmarkProvider(null as any);
|
||||
const engine = new ScreenerEngine(client as any, benchmark, { logger: noopLogger });
|
||||
|
||||
const results = await engine.screenTickers([]);
|
||||
assert.equal(results.STOCK.length, 0);
|
||||
assert.equal(results.ETF.length, 0);
|
||||
assert.equal(results.BOND.length, 0);
|
||||
assert.equal(results.ERROR.length, 0);
|
||||
});
|
||||
|
||||
await t.test('screenTickers() processes multiple tickers', async () => {
|
||||
const client = new MockYahooClient();
|
||||
const benchmark = new MockBenchmarkProvider(null as any);
|
||||
const engine = new ScreenerEngine(client as any, benchmark, { logger: noopLogger });
|
||||
|
||||
const results = await engine.screenTickers(['AAPL', 'MSFT']);
|
||||
const totalResults =
|
||||
results.STOCK.length + results.ETF.length + results.BOND.length + results.ERROR.length;
|
||||
assert.equal(totalResults, 2);
|
||||
});
|
||||
|
||||
await t.test('screenWithProgress() works without logger', async () => {
|
||||
const client = new MockYahooClient();
|
||||
const benchmark = new MockBenchmarkProvider(null as any);
|
||||
const engine = new ScreenerEngine(client as any, benchmark, { logger: noopLogger });
|
||||
|
||||
const results = await engine.screenWithProgress(['AAPL']);
|
||||
assert.ok(results);
|
||||
assert.ok('marketContext' in results);
|
||||
});
|
||||
|
||||
await t.test('screenTickers() processes large ticker list correctly', async () => {
|
||||
const client = new MockYahooClient();
|
||||
const benchmark = new MockBenchmarkProvider(null as any);
|
||||
const engine = new ScreenerEngine(client as any, benchmark, { logger: noopLogger });
|
||||
|
||||
// Create array of 10 tickers (batch size is 5, so should need 2 batches)
|
||||
const tickers = Array(10)
|
||||
.fill(0)
|
||||
.map((_, i) => (i % 2 === 0 ? 'AAPL' : 'MSFT'));
|
||||
const results = await engine.screenTickers(tickers);
|
||||
|
||||
const totalResults =
|
||||
results.STOCK.length + results.ETF.length + results.BOND.length + results.ERROR.length;
|
||||
assert.equal(totalResults, 10);
|
||||
});
|
||||
|
||||
await t.test('screenTickers() includes scoring details', async () => {
|
||||
const client = new MockYahooClient();
|
||||
const benchmark = new MockBenchmarkProvider(null as any);
|
||||
const engine = new ScreenerEngine(client as any, benchmark, { logger: noopLogger });
|
||||
|
||||
const results = await engine.screenTickers(['AAPL']);
|
||||
if (results.STOCK.length > 0) {
|
||||
const result = results.STOCK[0];
|
||||
assert.ok(result.signal);
|
||||
assert.ok(result.fundamental);
|
||||
assert.ok(result.inflated);
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user