Assessor_2.0/tests/postgres/migration-runner.test.ts

42 lines
2.2 KiB
TypeScript

import { createHash } from 'node:crypto';
import type { Pool, PoolClient, QueryResult } from 'pg';
import { describe, expect, it, vi } from 'vitest';
import { runDatabaseMigrations } from '../../server/postgres/migration-runner';
function result(rows: unknown[] = []): QueryResult {
return { command: '', rowCount: rows.length, oid: 0, fields: [], rows } as QueryResult;
}
function fakePool(existing: Array<{ version: string; checksum: string }> = []) {
const query = vi.fn(async (text: string) => text.includes('SELECT version, checksum') ? result(existing) : result());
const release = vi.fn();
const client = { query, release } as unknown as PoolClient;
const pool = { connect: vi.fn(async () => client) } as unknown as Pool;
return { pool, query, release };
}
describe('PostgreSQL migration runner', () => {
it('serializes, applies and records a pending migration transactionally', async () => {
const runtime = fakePool();
const applied = await runDatabaseMigrations(runtime.pool, [{ version: '900', name: 'test', sql: 'SELECT 900' }]);
expect(applied).toEqual([{ version: '900', name: 'test', checksum: createHash('sha256').update('SELECT 900').digest('hex') }]);
const statements = runtime.query.mock.calls.map(call => call[0]);
expect(statements).toContain('BEGIN');
expect(statements).toContain('SELECT 900');
expect(statements).toContain('COMMIT');
expect(statements.some(statement => statement.includes('INSERT INTO assessor_schema_migrations'))).toBe(true);
expect(statements.some(statement => statement.includes('pg_advisory_lock'))).toBe(true);
expect(statements.some(statement => statement.includes('pg_advisory_unlock'))).toBe(true);
expect(runtime.release).toHaveBeenCalledOnce();
});
it('rejects a changed migration checksum without executing its SQL', async () => {
const runtime = fakePool([{ version: '900', checksum: 'different' }]);
await expect(runDatabaseMigrations(runtime.pool, [{ version: '900', name: 'test', sql: 'SELECT 900' }]))
.rejects.toThrow('nach ihrer Anwendung verändert');
expect(runtime.query.mock.calls.map(call => call[0])).not.toContain('SELECT 900');
expect(runtime.release).toHaveBeenCalledOnce();
});
});