93 lines
3.1 KiB
TypeScript
93 lines
3.1 KiB
TypeScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { buildApp } from '../server/app.js';
|
|
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;
|
|
|
|
test('App Bootstrap', async (t) => {
|
|
await t.test('builds successfully without logger', async () => {
|
|
const app = await buildApp({ logger: false, db: mockDb });
|
|
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 response = await app.inject({
|
|
method: 'GET',
|
|
url: '/health',
|
|
});
|
|
assert.equal(response.statusCode, 200);
|
|
const body = JSON.parse(response.body);
|
|
assert.equal(body.status, 'ok');
|
|
});
|
|
|
|
await t.test('POST /api/screen requires valid schema', async () => {
|
|
const app = await buildApp({ logger: false, db: mockDb });
|
|
const response = await app.inject({
|
|
method: 'POST',
|
|
url: '/api/screen',
|
|
payload: { tickers: [] }, // Empty array fails minItems: 1
|
|
});
|
|
// Empty array is invalid per schema (minItems: 1)
|
|
assert.equal(response.statusCode, 400);
|
|
});
|
|
|
|
await t.test('POST /api/screen rejects invalid payload', async () => {
|
|
const app = await buildApp({ logger: false, db: mockDb });
|
|
const response = await app.inject({
|
|
method: 'POST',
|
|
url: '/api/screen',
|
|
payload: { invalid: 'data' },
|
|
});
|
|
assert.equal(response.statusCode, 400);
|
|
});
|
|
|
|
await t.test('GET /api/screen/catalysts returns results', async () => {
|
|
const app = await buildApp({ logger: false, db: mockDb });
|
|
const response = await app.inject({
|
|
method: 'GET',
|
|
url: '/api/screen/catalysts',
|
|
});
|
|
assert.equal(response.statusCode, 200);
|
|
const body = JSON.parse(response.body);
|
|
assert.ok('tickers' in body);
|
|
assert.ok('stories' in body);
|
|
});
|
|
|
|
await t.test('CORS is enabled for configured origin', async () => {
|
|
const app = await buildApp({ logger: false, db: mockDb });
|
|
const response = await app.inject({
|
|
method: 'GET',
|
|
url: '/health',
|
|
headers: {
|
|
origin: 'http://localhost:5173',
|
|
},
|
|
});
|
|
assert.ok(response.headers['access-control-allow-origin']);
|
|
});
|
|
|
|
await t.test('API key auth is optional (disabled by default)', async () => {
|
|
const app = await buildApp({ logger: false, db: mockDb });
|
|
const response = await app.inject({
|
|
method: 'GET',
|
|
url: '/health',
|
|
});
|
|
// Should work without API key when not configured
|
|
assert.equal(response.statusCode, 200);
|
|
});
|
|
|
|
await t.test('OPTIONS requests bypass auth check', async () => {
|
|
const app = await buildApp({ logger: false, db: mockDb });
|
|
const response = await app.inject({
|
|
method: 'OPTIONS',
|
|
url: '/api/screen',
|
|
headers: { origin: 'http://localhost:5173' },
|
|
});
|
|
// OPTIONS preflight should not be blocked by auth (any non-401/403 is fine)
|
|
assert.ok(response.statusCode !== 401 && response.statusCode !== 403);
|
|
});
|
|
});
|