feat(F-002): database foundation with migrations and dev compose
- node-pg-migrate + pg: baseline migration (extensions, app_meta) with working down - src/infrastructure/db fail-fast pool and typed query helper - docker-compose: postgres:16-alpine + redis:7-alpine with one-command up - table naming convention <module>_<table> documented in README - integration tests (6) against real PostgreSQL; strict identifier validation for test DDL after security-gate hardening round - deps justified in spec/tech.md; all gates approved; verify.sh green
This commit is contained in:
37
project/src/infrastructure/db/tests/migrations.itest.ts
Normal file
37
project/src/infrastructure/db/tests/migrations.itest.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import pg from 'pg';
|
||||
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
||||
import { getTestDbUrl, recreateDatabase, runMigrations, tableExists } from './db-test-support.js';
|
||||
|
||||
const hasDb = Boolean(process.env.TEST_DATABASE_URL);
|
||||
|
||||
describe.skipIf(!hasDb)('migrations', () => {
|
||||
const url = hasDb ? getTestDbUrl() : '';
|
||||
let pool: pg.Pool;
|
||||
|
||||
beforeAll(async () => {
|
||||
await recreateDatabase(url);
|
||||
pool = new pg.Pool({ connectionString: url, max: 2 });
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await pool.end();
|
||||
});
|
||||
|
||||
it('fresh up creates the baseline schema', async () => {
|
||||
await runMigrations(url, 'up');
|
||||
expect(await tableExists(pool, 'app_meta')).toBe(true);
|
||||
});
|
||||
|
||||
it('second up is a no-op', async () => {
|
||||
const before = await pool.query('SELECT count(*)::int AS n FROM pgmigrations');
|
||||
await runMigrations(url, 'up');
|
||||
const after = await pool.query('SELECT count(*)::int AS n FROM pgmigrations');
|
||||
expect(after.rows[0]?.n).toBe(before.rows[0]?.n);
|
||||
expect(await tableExists(pool, 'app_meta')).toBe(true);
|
||||
});
|
||||
|
||||
it('down rolls back the baseline schema cleanly', async () => {
|
||||
await runMigrations(url, 'down');
|
||||
expect(await tableExists(pool, 'app_meta')).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user