phase-10.5: test case fixes and updated postman collection
This commit is contained in:
+20
-10
@@ -40,6 +40,8 @@ import {
|
||||
interface BuildAppOptions {
|
||||
logger?: boolean;
|
||||
db?: DatabaseConnection;
|
||||
/** Inject a stub in tests to avoid live Yahoo news fetches. */
|
||||
catalystCache?: CatalystCache;
|
||||
}
|
||||
|
||||
// ── JWT auth helpers ─────────────────────────────────────────────────────────
|
||||
@@ -78,7 +80,11 @@ function makeRoleGuard(required: 'trader' | 'admin') {
|
||||
// 3. Create barrel: server/domains/<domain>/index.ts
|
||||
// 4. Import from domain and register controller below
|
||||
// ───────────────────────────────────────────────────────────────────────────
|
||||
export async function buildApp({ logger = true, db: injectedDb }: BuildAppOptions = {}) {
|
||||
export async function buildApp({
|
||||
logger = true,
|
||||
db: injectedDb,
|
||||
catalystCache: injectedCache,
|
||||
}: BuildAppOptions = {}) {
|
||||
const app = Fastify({ logger });
|
||||
|
||||
await app.register(cors, {
|
||||
@@ -126,7 +132,7 @@ export async function buildApp({ logger = true, db: injectedDb }: BuildAppOption
|
||||
const advisor = new PortfolioAdvisor(yahoo);
|
||||
const calSvc = new CalendarService(yahoo);
|
||||
const llm = new LLMAnalyst({ logger: noopLogger });
|
||||
const catalystCache = new CatalystCache({ logger: noopLogger }); // Singleton, cached for 15m
|
||||
const catalystCache = injectedCache ?? new CatalystCache({ logger: noopLogger }); // Singleton, 15m cache
|
||||
|
||||
// Auth domain — generate a fresh invite code on every boot and print it
|
||||
const INVITE_CODE = randomBytes(12).toString('hex'); // 24-char hex string
|
||||
@@ -136,14 +142,18 @@ export async function buildApp({ logger = true, db: injectedDb }: BuildAppOption
|
||||
const innerWidth = Math.max(line1.length, line2.length) + 2;
|
||||
const hr = '─'.repeat(innerWidth);
|
||||
const pad = (s: string) => `│ ${s}${' '.repeat(innerWidth - 1 - s.length)}│`;
|
||||
/* eslint-disable no-console -- boot-time invite code must reach the operator's terminal */
|
||||
console.log(`\n┌${hr}┐`);
|
||||
console.log(pad(''));
|
||||
console.log(pad(line1));
|
||||
console.log(pad(line2));
|
||||
console.log(pad(''));
|
||||
console.log(`└${hr}┘\n`);
|
||||
/* eslint-enable no-console */
|
||||
// Never print the invite code when the logger is disabled (tests) — secrets
|
||||
// don't belong in test output.
|
||||
if (logger !== false) {
|
||||
/* eslint-disable no-console -- boot-time invite code must reach the operator's terminal */
|
||||
console.log(`\n┌${hr}┐`);
|
||||
console.log(pad(''));
|
||||
console.log(pad(line1));
|
||||
console.log(pad(line2));
|
||||
console.log(pad(''));
|
||||
console.log(`└${hr}┘\n`);
|
||||
/* eslint-enable no-console */
|
||||
}
|
||||
|
||||
const userStore = new UserStore(db);
|
||||
const authService = new AuthService(userStore, JWT_SECRET);
|
||||
|
||||
@@ -35,12 +35,14 @@ export class ScreenerEngine {
|
||||
private static readonly BATCH_DELAY_MS = 1000;
|
||||
|
||||
private logger: Logger;
|
||||
private readonly batchDelayMs: number;
|
||||
|
||||
constructor(
|
||||
private readonly client: YahooFinanceClient,
|
||||
private readonly benchmarkProvider: BenchmarkProvider,
|
||||
{ logger }: ScreenerEngineOptions = {},
|
||||
{ logger, batchDelayMs }: ScreenerEngineOptions = {},
|
||||
) {
|
||||
this.batchDelayMs = batchDelayMs ?? ScreenerEngine.BATCH_DELAY_MS;
|
||||
// eslint-disable-next-line no-console
|
||||
this.logger = logger ?? {
|
||||
write: (msg: string) => process.stdout.write(msg),
|
||||
@@ -65,11 +67,12 @@ export class ScreenerEngine {
|
||||
const chunks = chunkArray(tickers, ScreenerEngine.BATCH_SIZE);
|
||||
let processed = 0;
|
||||
|
||||
for (const chunk of chunks) {
|
||||
await this.processBatch(chunk, marketContext, results);
|
||||
processed += chunk.length;
|
||||
for (let i = 0; i < chunks.length; i++) {
|
||||
await this.processBatch(chunks[i], marketContext, results);
|
||||
processed += chunks[i].length;
|
||||
this.logProgress(showProgress, processed, tickers.length);
|
||||
await this.rateLimitDelay();
|
||||
// Rate-limit pause between batches — never after the last one
|
||||
if (i < chunks.length - 1) await this.rateLimitDelay();
|
||||
}
|
||||
|
||||
if (showProgress) {
|
||||
@@ -110,7 +113,8 @@ export class ScreenerEngine {
|
||||
}
|
||||
|
||||
private async rateLimitDelay(): Promise<void> {
|
||||
await new Promise<void>((r) => setTimeout(r, ScreenerEngine.BATCH_DELAY_MS));
|
||||
if (this.batchDelayMs <= 0) return;
|
||||
await new Promise<void>((r) => setTimeout(r, this.batchDelayMs));
|
||||
}
|
||||
|
||||
private async fetch(ticker: string): Promise<MappedData | ErrorResult> {
|
||||
|
||||
@@ -90,4 +90,6 @@ export interface RuleSet {
|
||||
// ── ScreenerEngine ────────────────────────────────────────────────────────
|
||||
export interface ScreenerEngineOptions {
|
||||
logger?: Logger;
|
||||
/** Delay between Yahoo batches (ms). Default 1000; set 0 in tests. */
|
||||
batchDelayMs?: number;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user