Files
pn-new-crm/tests/unit/services/pg-dump-runner.test.ts

64 lines
2.2 KiB
TypeScript
Raw Normal View History

/**
* Unit test for `runPgDump` in backup.service.ts.
*
* Regression: the dump promise must resolve once BOTH (a) the child process
* exits 0 and (b) the output file is fully flushed regardless of which event
* fires first. The original implementation attached the file's `finish`
* listener *inside* the child `close` handler, but `stdout.pipe(out)`
* auto-ends the file when the child's stdout closes, so `finish` frequently
* fired before the listener was attached the promise hung forever (observed
* end-to-end against a real pg_dump).
*
* We drive the spawn with `node` instead of `pg_dump` (injected via opts) so
* the test is deterministic and needs no database.
*/
import { readFileSync, mkdtempSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import path from 'node:path';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { runPgDump } from '@/lib/services/backup.service';
describe('runPgDump', () => {
let dir: string;
beforeEach(() => {
dir = mkdtempSync(path.join(tmpdir(), 'pn-pgdump-'));
});
afterEach(() => {
rmSync(dir, { recursive: true, force: true });
});
it('resolves and writes the child stdout to the output file on exit 0', async () => {
const out = path.join(dir, 'a.dump');
await runPgDump('ignored://url', out, {
command: process.execPath,
buildArgs: () => ['-e', 'process.stdout.write("DUMP-CONTENTS-1234")'],
});
expect(readFileSync(out, 'utf8')).toBe('DUMP-CONTENTS-1234');
});
it('rejects with the captured stderr when the child exits non-zero', async () => {
const out = path.join(dir, 'b.dump');
await expect(
runPgDump('ignored://url', out, {
command: process.execPath,
buildArgs: () => ['-e', 'process.stderr.write("kaboom"); process.exit(3)'],
}),
).rejects.toThrow(/exited 3.*kaboom/s);
});
it('rejects when the command cannot be spawned', async () => {
const out = path.join(dir, 'c.dump');
await expect(
runPgDump('ignored://url', out, {
command: '/nonexistent/definitely-not-a-real-binary-xyz',
buildArgs: () => [],
}),
).rejects.toBeTruthy();
});
});