Files
redefined-designs/backend/tests/integration/errorHandling.integration.test.ts
T
bermudalambandClaude Opus 5 c77fdad2b9 fix: run migrations on boot and stop failures rendering as empty (#23)
The storefront showed no inventory after deploying the categories/tags
release. No data was lost: the code queried categories/item_tags/
items.category_id against a database where the migration had not been
run, and that failure was invisible at every layer.

Three changes, each addressing one layer:

Migrations now run at container start, so deployed code cannot be ahead
of the schema and the easily-forgotten manual `docker exec migrate.js
up` step disappears. migrate.js waits for Postgres to accept
connections first, since the NAS brings the DB container up slower than
the app, and still exits non-zero so a bad migration stops the
container rather than serving a half-migrated schema.

Express 4 does not forward a rejected async handler, and no error
middleware was mounted, so a failing query never responded at all. Async
routes are now wrapped and an error middleware guarantees a 500. A hung
request is indistinguishable from an empty result in the UI, which is
how a schema mismatch came to read as "the store has no items".

The storefront now separates "request failed" from "no items" and offers
a retry. fetchItems/fetchFilterOptions throw on a non-OK response rather
than returning the parsed error body, which would have been set as the
item list and crashed the grid on .map.

Also restores the project-context update from 7fb5764, which was left
out of PR #24 and ended up dangling.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 10:41:24 -05:00

35 lines
1.2 KiB
TypeScript

import request from 'supertest';
import app from '../../src/app';
import { pool } from '../../src/db';
import { resetDb, closeDb } from './setup/testDb';
beforeEach(async () => {
await resetDb();
});
afterAll(async () => {
await pool.end();
await closeDb();
});
describe('unexpected route failures', () => {
it('answers with 500 instead of leaving the request hanging', async () => {
// A non-numeric id reaches Postgres as `WHERE i.id = 'not-a-number'`, which
// raises invalid-input-syntax. Express 4 does not forward a rejected async
// handler on its own, so without the asyncRoute wrapper plus the error
// middleware this request never gets a response at all — and a hung request
// renders as an empty storefront rather than a visible failure.
const res = await request(app).get('/api/items/not-a-number');
expect(res.status).toBe(500);
expect(res.body.error).toBe('internal error');
});
it('does not leak the underlying database error to the client', async () => {
const res = await request(app).get('/api/items/not-a-number');
expect(JSON.stringify(res.body)).not.toContain('syntax');
expect(JSON.stringify(res.body)).not.toContain('items');
});
});