scripts/local-stop.test.mjs
Rama main · réplica de lectura del repositorio original.
import assert from 'node:assert/strict';
import { spawn, spawnSync } from 'node:child_process';
import { once } from 'node:events';
import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import test from 'node:test';
async function fixture(t, owned) {
const root = mkdtempSync(join(tmpdir(), 'devpush-stop-'));
t.after(() => rmSync(root, { recursive: true, force: true }));
for (const directory of ['scripts', '.tmp', 'app/scripts', 'app/node_modules/vite/bin', 'bin'])
mkdirSync(join(root, directory), { recursive: true });
writeFileSync(join(root, 'scripts/local-stop.sh'), readFileSync(new URL('./local-stop.sh', import.meta.url)));
writeFileSync(join(root, 'app/scripts/fs-watch-error-guard.cjs'), '');
writeFileSync(join(root, 'app/node_modules/vite/bin/vite.js'), 'setInterval(() => {}, 1000)');
const args = owned
? ['-r', './scripts/fs-watch-error-guard.cjs', './node_modules/vite/bin/vite.js', 'dev', '--port', '7429', '--strictPort']
: ['-e', 'setInterval(() => {}, 1000)'];
const child = spawn(process.execPath, args, { cwd: join(root, 'app'), stdio: 'ignore' });
await once(child, 'spawn');
t.after(async () => {
if (child.exitCode === null && child.signalCode === null) {
const exited = once(child, 'exit');
child.kill();
await exited;
}
});
writeFileSync(join(root, '.tmp/devpush-dev.pid'), String(child.pid));
// Substitute only lsof, so these tests never inspect or stop real listeners.
writeFileSync(join(root, 'bin/lsof'), `#!/bin/sh\ncase "$*" in\n *cwd*) printf '%s\\n' 'n${root}/app' ;;\n *) printf '%s\\n' '${child.pid}' ;;\nesac\n`, { mode: 0o755 });
return { root, child, run: () => spawnSync('sh', [join(root, 'scripts/local-stop.sh')], {
env: { ...process.env, PATH: `${root}/bin:${process.env.PATH}` }, encoding: 'utf8', timeout: 10000,
}) };
}
test('stale PID and unrelated port listener are left running', async (t) => {
const { child, run } = await fixture(t, false);
const result = run();
assert.equal(result.status, 0, result.stderr);
assert.doesNotThrow(() => process.kill(child.pid, 0));
});
test('stops the devpush Vite process belonging to this checkout', { timeout: 15000 }, async (t) => {
const { child, run } = await fixture(t, true);
const exited = once(child, 'exit');
const result = run();
assert.equal(result.status, 0, result.stderr);
let deadline;
try {
await Promise.race([
exited,
new Promise((_, reject) => {
deadline = setTimeout(() => reject(new Error('Devpush fixture was not stopped')), 2000);
}),
]);
} finally {
clearTimeout(deadline);
}
assert.equal(child.signalCode, 'SIGTERM');
});
test('a matching command from another checkout is left running', async (t) => {
const { root, child, run } = await fixture(t, true);
writeFileSync(join(root, 'bin/lsof'), `#!/bin/sh\nprintf '%s\\n' 'n/another-checkout/app'\n`, { mode: 0o755 });
const result = run();
assert.equal(result.status, 0, result.stderr);
assert.doesNotThrow(() => process.kill(child.pid, 0));
});