insforge--insforge
89 行
3.6 KiB
TypeScript
89 行
3.6 KiB
TypeScript
import {
|
|
getConnections as getInsforgeConnections,
|
|
seed,
|
|
type GetConnectionResult,
|
|
} from 'insforge-test';
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
const ROOT = path.resolve(__dirname, '..', '..', '..');
|
|
const MIGRATIONS = path.join(ROOT, 'backend/src/infra/database/migrations');
|
|
|
|
const migrationFiles = fs
|
|
.readdirSync(MIGRATIONS)
|
|
.filter((f) => f.endsWith('.sql'))
|
|
.sort()
|
|
.map((f) => path.join(MIGRATIONS, f));
|
|
|
|
/**
|
|
* Pre-configured getConnections() for InsForge integration tests.
|
|
*
|
|
* Seeds the test database by running the full InsForge migration chain
|
|
* (db-init.sql + migrations 000–048) so the test schema matches production.
|
|
*
|
|
* pg_cron can only be installed in the database named by cron.database_name
|
|
* (the Docker image hardcodes "insforge"), but pgsql-test creates isolated
|
|
* databases with random names. A stub cron schema provides the table and
|
|
* function signatures that later migrations reference. The real pgcrypto
|
|
* and http extensions are installed normally.
|
|
*
|
|
* CREATE EXTENSION statements are stripped from migration files because
|
|
* extensions are handled in the pre-seed step above.
|
|
*/
|
|
export const getConnections = (
|
|
opts: Parameters<typeof getInsforgeConnections>[0] = {}
|
|
): Promise<GetConnectionResult> =>
|
|
getInsforgeConnections(opts, [
|
|
// 1. Bootstrap roles and event triggers
|
|
seed.sqlfile([path.join(ROOT, 'deploy/docker-init/db/db-init.sql')]),
|
|
// 2. Install real extensions + stub pg_cron
|
|
//
|
|
// pgcrypto and http are real extensions from the Docker image.
|
|
//
|
|
// pg_cron cannot be installed here because CREATE EXTENSION pg_cron
|
|
// only works in the database named by cron.database_name (hardcoded
|
|
// to "insforge" in postgresql.conf), and pgsql-test creates isolated
|
|
// databases with random names. We stub the cron schema because
|
|
// migrations 024 and 041 execute DO $$ blocks at migration time
|
|
// that SELECT FROM cron.job and PERFORM cron.schedule()/unschedule().
|
|
seed.fn(async (ctx) => {
|
|
await ctx.pg.query(`
|
|
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
|
CREATE EXTENSION IF NOT EXISTS http;
|
|
|
|
CREATE SCHEMA IF NOT EXISTS cron;
|
|
CREATE TABLE IF NOT EXISTS cron.job (
|
|
jobid bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
|
schedule text NOT NULL,
|
|
command text NOT NULL,
|
|
nodename text DEFAULT 'localhost',
|
|
nodeport int DEFAULT 5432,
|
|
database text DEFAULT current_database(),
|
|
username text DEFAULT current_user,
|
|
active boolean DEFAULT true,
|
|
jobname text
|
|
);
|
|
CREATE FUNCTION cron.schedule(cron_schedule text, command text)
|
|
RETURNS bigint LANGUAGE sql AS $$ SELECT 0::bigint $$;
|
|
CREATE FUNCTION cron.schedule(job_name text, cron_schedule text, command text)
|
|
RETURNS bigint LANGUAGE sql AS $$ SELECT 0::bigint $$;
|
|
CREATE FUNCTION cron.unschedule(job_id bigint)
|
|
RETURNS boolean LANGUAGE sql AS $$ SELECT true $$;
|
|
CREATE FUNCTION cron.unschedule(job_name text)
|
|
RETURNS boolean LANGUAGE sql AS $$ SELECT true $$;
|
|
`);
|
|
}),
|
|
// 3. Run the full migration chain (000–048), stripping CREATE EXTENSION
|
|
// statements since extensions are already installed or stubbed above.
|
|
seed.fn(async (ctx) => {
|
|
for (const file of migrationFiles) {
|
|
const sql = fs
|
|
.readFileSync(file, 'utf8')
|
|
.replace(/^CREATE EXTENSION IF NOT EXISTS \S+;$/gm, '');
|
|
await ctx.pg.query(sql);
|
|
}
|
|
}),
|
|
]);
|