import { test } from 'node:test'; import assert from 'node:assert/strict'; import { StockScorer } from '../server/screener/scorers/StockScorer.js'; const baseRules = { gates: { maxDebtToEquity: 3.0, minQuickRatio: 0.5, maxPERatio: 20, maxPegGate: 1.5 }, weights: { margin: 2, opMargin: 2, roe: 3, peg: 2, revenue: 2, fcf: 2 }, thresholds: { marginHigh: 20, marginMed: 10, opMarginHigh: 20, opMarginMed: 10, roeHigh: 20, roeMed: 10, pegHigh: 1.0, pegMed: 1.5, revHigh: 15, revMed: 5, fcfHigh: 5, fcfMed: 2, }, }; const pass = { peRatio: 15, pegRatio: 1.2, debtToEquity: 1.0, quickRatio: 1.0, returnOnEquity: 22, operatingMargin: 25, netProfitMargin: 18, revenueGrowth: 16, fcfYield: 6, }; test('rejects on high D/E', () => { const result = StockScorer.score({ ...pass, debtToEquity: 4.0 }, baseRules); assert.equal(result.label, '🔴 REJECT'); assert(result.scoreSummary.includes('D/E')); }); test('rejects on high P/E', () => { const result = StockScorer.score({ ...pass, peRatio: 25 }, baseRules); assert.equal(result.label, '🔴 REJECT'); assert(result.scoreSummary.includes('P/E')); }); test('rejects on high PEG', () => { const result = StockScorer.score({ ...pass, pegRatio: 2.0 }, baseRules); assert.equal(result.label, '🔴 REJECT'); }); test('skips gate when metric is null (missing data)', () => { const result = StockScorer.score({ ...pass, pegRatio: null, peRatio: null }, baseRules); assert.notEqual(result.label, '🔴 REJECT'); }); test('high-conviction BUY on strong metrics', () => { const result = StockScorer.score(pass, baseRules); assert.equal(result.label, '🟢 BUY (High Conviction)'); }); test('audit breakdown contains scored factors', () => { const result = StockScorer.score(pass, baseRules); assert(result.audit.passedGates); assert(result.audit.breakdown.roe != null); assert(result.audit.breakdown.margin != null); }); test('beta > 1.5 surfaces as risk flag', () => { const result = StockScorer.score({ ...pass, beta: 2.0 }, baseRules); assert(result.audit.riskFlags?.some((f) => f.includes('High volatility'))); }); test('near 52-week high surfaces as risk flag', () => { const result = StockScorer.score( { ...pass, week52High: 200, week52Low: 100, currentPrice: 195 }, baseRules, ); assert(result.audit.riskFlags?.some((f) => f.includes('52-week high'))); });