fix bruno collection

This commit is contained in:
Kazuma
2026-06-06 21:49:31 -04:00
parent 2e7860637e
commit 76c2a671f4
25 changed files with 4361 additions and 94 deletions
+196
View File
@@ -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);
}
});
});