phase-10.5: test case fixes and updated postman collection
This commit is contained in:
@@ -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
@@ -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',
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user