phase-10.5: test case fixes and updated postman collection

This commit is contained in:
saikiranvella
2026-06-12 00:47:41 -04:00
parent 65907a9b8d
commit 6cb4c93a0e
8 changed files with 524 additions and 668 deletions
+8 -3
View File
@@ -13,6 +13,11 @@ const MOCK_LLM_RESPONSE = JSON.stringify({
const mockDb = new MockDatabaseConnection() as never;
// Stub catalyst cache — no live Yahoo news fetches in tests (fast + offline)
const stubCatalystCache = {
get: async () => ({ tickers: [] as string[], stories: [] }),
} as never;
test('POST /api/analyze', async (t) => {
// Spy on AnthropicClient.prototype.complete before buildApp wires it up.
// This prevents any real API calls during tests.
@@ -26,7 +31,7 @@ test('POST /api/analyze', async (t) => {
mock.method(AnthropicClient.prototype, 'isAvailable', () => true, { getter: true });
await t.test('returns analysis when stories match tickers', async () => {
const app = await buildApp({ logger: false, db: mockDb });
const app = await buildApp({ logger: false, db: mockDb, catalystCache: stubCatalystCache });
const response = await app.inject({
method: 'POST',
@@ -47,7 +52,7 @@ test('POST /api/analyze', async (t) => {
// Reset the isAvailable mock to simulate no API key
mock.method(AnthropicClient.prototype, 'isAvailable', () => false, { getter: true });
const app = await buildApp({ logger: false, db: mockDb });
const app = await buildApp({ logger: false, db: mockDb, catalystCache: stubCatalystCache });
const response = await app.inject({
method: 'POST',
@@ -68,7 +73,7 @@ test('POST /api/analyze', async (t) => {
mock.method(AnthropicClient.prototype, 'isAvailable', () => true, { getter: true });
const callsBefore = completeSpy.mock.calls.length;
const app = await buildApp({ logger: false, db: mockDb });
const app = await buildApp({ logger: false, db: mockDb, catalystCache: stubCatalystCache });
await app.inject({
method: 'POST',
+13 -8
View File
@@ -6,15 +6,20 @@ import { MockDatabaseConnection } from './helpers/mockDb.js';
// Inject mock DB so tests don't require the native better-sqlite3 binary
const mockDb = new MockDatabaseConnection() as never;
// Stub catalyst cache — no live Yahoo news fetches in tests (fast + offline)
const stubCatalystCache = {
get: async () => ({ tickers: [] as string[], stories: [] }),
} as never;
test('App Bootstrap', async (t) => {
await t.test('builds successfully without logger', async () => {
const app = await buildApp({ logger: false, db: mockDb });
const app = await buildApp({ logger: false, db: mockDb, catalystCache: stubCatalystCache });
assert.ok(app);
assert.ok(app.server);
});
await t.test('health check endpoint returns 200', async () => {
const app = await buildApp({ logger: false, db: mockDb });
const app = await buildApp({ logger: false, db: mockDb, catalystCache: stubCatalystCache });
const response = await app.inject({
method: 'GET',
url: '/health',
@@ -25,7 +30,7 @@ test('App Bootstrap', async (t) => {
});
await t.test('POST /api/screen requires valid schema', async () => {
const app = await buildApp({ logger: false, db: mockDb });
const app = await buildApp({ logger: false, db: mockDb, catalystCache: stubCatalystCache });
const response = await app.inject({
method: 'POST',
url: '/api/screen',
@@ -36,7 +41,7 @@ test('App Bootstrap', async (t) => {
});
await t.test('POST /api/screen rejects invalid payload', async () => {
const app = await buildApp({ logger: false, db: mockDb });
const app = await buildApp({ logger: false, db: mockDb, catalystCache: stubCatalystCache });
const response = await app.inject({
method: 'POST',
url: '/api/screen',
@@ -46,7 +51,7 @@ test('App Bootstrap', async (t) => {
});
await t.test('GET /api/screen/catalysts returns results', async () => {
const app = await buildApp({ logger: false, db: mockDb });
const app = await buildApp({ logger: false, db: mockDb, catalystCache: stubCatalystCache });
const response = await app.inject({
method: 'GET',
url: '/api/screen/catalysts',
@@ -58,7 +63,7 @@ test('App Bootstrap', async (t) => {
});
await t.test('CORS is enabled for configured origin', async () => {
const app = await buildApp({ logger: false, db: mockDb });
const app = await buildApp({ logger: false, db: mockDb, catalystCache: stubCatalystCache });
const response = await app.inject({
method: 'GET',
url: '/health',
@@ -70,7 +75,7 @@ test('App Bootstrap', async (t) => {
});
await t.test('API key auth is optional (disabled by default)', async () => {
const app = await buildApp({ logger: false, db: mockDb });
const app = await buildApp({ logger: false, db: mockDb, catalystCache: stubCatalystCache });
const response = await app.inject({
method: 'GET',
url: '/health',
@@ -80,7 +85,7 @@ test('App Bootstrap', async (t) => {
});
await t.test('OPTIONS requests bypass auth check', async () => {
const app = await buildApp({ logger: false, db: mockDb });
const app = await buildApp({ logger: false, db: mockDb, catalystCache: stubCatalystCache });
const response = await app.inject({
method: 'OPTIONS',
url: '/api/screen',
+22
View File
@@ -84,6 +84,28 @@ test('BondScorer', async (t) => {
assert.ok(resultLong.audit?.passedGates);
});
await t.test('returns structured tier (P0.3)', () => {
const good: BondMetrics = {
ytm: 7.5, // 7.5% vs ~4% risk-free → wide spread
duration: 5,
creditRating: 'A',
creditRatingNumeric: 8,
};
const pass = BondScorer.score(good, DEFAULT_RULES);
assert.equal(pass.tier, 'PASS');
assert.equal(typeof pass.score, 'number');
const junk: BondMetrics = {
ytm: 8,
duration: 5,
creditRating: 'CCC',
creditRatingNumeric: 4, // below investment-grade gate
};
const reject = BondScorer.score(junk, DEFAULT_RULES);
assert.equal(reject.tier, 'REJECT');
assert.equal(reject.score, null);
});
await t.test('handles null/undefined metrics gracefully', () => {
const metrics = {
ytm: null,