phase-8:server code enhancements.
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* Integration tests for ScreenerController + /health
|
||||
* Uses Fastify inject() — no real Yahoo calls, no live server.
|
||||
*/
|
||||
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import Fastify from 'fastify';
|
||||
import cors from '@fastify/cors';
|
||||
import { ScreenerController } from '../server/controllers/screener.controller';
|
||||
import type { ScreenerEngine } from '../server/services/ScreenerEngine';
|
||||
import type { ScreenerResult, MarketContext } from '../server/types';
|
||||
|
||||
// ── Fixture data ────────────────────────────────────────────────────────────
|
||||
|
||||
const MARKET_CTX: MarketContext = {
|
||||
sp500Price: 5000,
|
||||
riskFreeRate: 4.5,
|
||||
vixLevel: 18,
|
||||
rateRegime: 'NORMAL',
|
||||
volatilityRegime: 'NORMAL',
|
||||
benchmarks: { marketPE: 22, techPE: 30, reitYield: 3.5, igSpread: 1.0 },
|
||||
};
|
||||
|
||||
const EMPTY_RESULT: ScreenerResult = {
|
||||
STOCK: [],
|
||||
ETF: [],
|
||||
BOND: [],
|
||||
ERROR: [],
|
||||
marketContext: MARKET_CTX,
|
||||
};
|
||||
|
||||
// ── Stub ────────────────────────────────────────────────────────────────────
|
||||
|
||||
const stubEngine = {
|
||||
screenTickers: async (_tickers: string[]) => EMPTY_RESULT,
|
||||
getMarketContext: async () => MARKET_CTX,
|
||||
} as unknown as ScreenerEngine;
|
||||
|
||||
// ── App factory ─────────────────────────────────────────────────────────────
|
||||
|
||||
async function buildTestApp() {
|
||||
const app = Fastify({ logger: false });
|
||||
await app.register(cors, { origin: '*' });
|
||||
new ScreenerController(stubEngine).register(app);
|
||||
app.get('/health', async () => ({ status: 'ok' }));
|
||||
await app.ready();
|
||||
return app;
|
||||
}
|
||||
|
||||
// ── Tests ────────────────────────────────────────────────────────────────────
|
||||
|
||||
test('GET /health → 200 { status: ok }', async () => {
|
||||
const app = await buildTestApp();
|
||||
const res = await app.inject({ method: 'GET', url: '/health' });
|
||||
assert.equal(res.statusCode, 200);
|
||||
assert.deepEqual(res.json(), { status: 'ok' });
|
||||
});
|
||||
|
||||
test('POST /api/screen → 200 with STOCK/ETF/BOND/ERROR/marketContext keys', async () => {
|
||||
const app = await buildTestApp();
|
||||
const res = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/api/screen',
|
||||
payload: { tickers: ['AAPL'] },
|
||||
});
|
||||
assert.equal(res.statusCode, 200);
|
||||
const body = res.json();
|
||||
assert.ok(Array.isArray(body.STOCK), 'STOCK should be array');
|
||||
assert.ok(Array.isArray(body.ETF), 'ETF should be array');
|
||||
assert.ok(Array.isArray(body.BOND), 'BOND should be array');
|
||||
assert.ok(Array.isArray(body.ERROR), 'ERROR should be array');
|
||||
assert.ok(body.marketContext, 'marketContext should be present');
|
||||
});
|
||||
|
||||
test('POST /api/screen → marketContext has expected shape', async () => {
|
||||
const app = await buildTestApp();
|
||||
const res = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/api/screen',
|
||||
payload: { tickers: ['MSFT'] },
|
||||
});
|
||||
const { marketContext } = res.json();
|
||||
assert.ok(typeof marketContext.riskFreeRate === 'number');
|
||||
assert.ok(typeof marketContext.sp500Price === 'number');
|
||||
assert.ok(typeof marketContext.vixLevel === 'number');
|
||||
assert.ok(marketContext.benchmarks);
|
||||
});
|
||||
|
||||
test('POST /api/screen with missing tickers → 400', async () => {
|
||||
const app = await buildTestApp();
|
||||
const res = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/api/screen',
|
||||
payload: {},
|
||||
});
|
||||
assert.equal(res.statusCode, 400);
|
||||
});
|
||||
|
||||
test('POST /api/screen with empty tickers array → 400', async () => {
|
||||
const app = await buildTestApp();
|
||||
const res = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/api/screen',
|
||||
payload: { tickers: [] },
|
||||
});
|
||||
assert.equal(res.statusCode, 400);
|
||||
});
|
||||
|
||||
test('POST /api/screen with too many tickers (>50) → 400', async () => {
|
||||
const app = await buildTestApp();
|
||||
const res = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/api/screen',
|
||||
payload: { tickers: Array.from({ length: 51 }, (_, i) => `T${i}`) },
|
||||
});
|
||||
assert.equal(res.statusCode, 400);
|
||||
});
|
||||
Reference in New Issue
Block a user