Skip to content

Commit 26562e3

Browse files
committed
test(host-kit): a vanished group answers with ESRCH, and no source-shape proxy
1 parent 29bcb0d commit 26562e3

1 file changed

Lines changed: 18 additions & 17 deletions

File tree

packages/host-kit/src/internal/exec-kill-settle.test.ts

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -167,17 +167,29 @@ test.runIf(process.platform !== 'win32')(
167167
//
168168
// The kill paths below address a process group whose leader this worker already reaped, and
169169
// the hermetic signal setup ends a worker's authority over a pid at that moment. So every
170-
// group write is answered by `guardGroupWrites` below, which is the seam that setup points a
171-
// real kill path at: it records what the kill aimed at and refuses to deliver it.
170+
// group write is answered by `guardGroupWrites` below, which is the seam that setup points a real
171+
// kill path at: it records what the kill aimed at and answers the way a real group would, either
172+
// a delivery nothing was reached for or the `ESRCH` a vanished group throws.
172173

173174
type GroupWrite = { readonly pid: number; readonly signal: string | number };
174175

175-
function guardGroupWrites(): { restore: () => void; writes: GroupWrite[] } {
176+
/** How a guarded group write answers, matching what a real group would do. */
177+
type GroupWriteAnswer = 'no-group-reached' | 'no-such-process';
178+
179+
function guardGroupWrites(answer: GroupWriteAnswer = 'no-group-reached'): {
180+
restore: () => void;
181+
writes: GroupWrite[];
182+
} {
176183
const original = process.kill.bind(process);
177184
const writes: GroupWrite[] = [];
178185
process.kill = ((pid: number, signal: string | number = 'SIGTERM') => {
179186
if (pid < 0) {
180187
writes.push({ pid, signal });
188+
if (answer === 'no-such-process') {
189+
const error = new Error('no such process') as NodeJS.ErrnoException;
190+
error.code = 'ESRCH';
191+
throw error;
192+
}
181193
return false;
182194
}
183195
return original(pid, signal as NodeJS.Signals);
@@ -200,17 +212,6 @@ test('group signaling addresses the negative pid and reports delivery', () => {
200212
}
201213
});
202214

203-
test('a group write in this module can only come from the seam', () => {
204-
// A second group-signal path in here would be the second seam the callers were written
205-
// against once more, and nothing at runtime distinguishes the two.
206-
const source = fs.readFileSync(new URL('./exec.ts', import.meta.url), 'utf8');
207-
const seam = source.indexOf('export function signalProcessGroupBestEffort');
208-
const writes = [...source.matchAll(/process\.kill\(-/g)].map((match) => match.index ?? -1);
209-
210-
assert.ok(seam >= 0);
211-
assert.deepEqual(writes, [seam + source.slice(seam).indexOf('process.kill(-')]);
212-
});
213-
214215
test('group signaling reports a vanished group and never signals an invalid pid', () => {
215216
const killSpy = vi.spyOn(process, 'kill').mockImplementation(() => {
216217
const error = new Error('not found') as NodeJS.ErrnoException;
@@ -263,10 +264,10 @@ test.runIf(process.platform !== 'win32')(
263264
test.runIf(process.platform !== 'win32')(
264265
'a detached deadline whose group cannot be signalled still settles',
265266
async () => {
266-
// A vanished group, an empty group, and a group owned by someone else all answer this
267-
// write with nothing, and the seam swallows that. The command still cannot wait on a pipe
267+
// A vanished group answers the group write by throwing `ESRCH`, and a group owned by someone
268+
// else by throwing `EPERM`; the seam swallows both. The command still cannot wait on a pipe
268269
// holder it just asked to be killed.
269-
const groupWrites = guardGroupWrites();
270+
const groupWrites = guardGroupWrites('no-such-process');
270271
try {
271272
const startedAt = Date.now();
272273
await assert.rejects(

0 commit comments

Comments
 (0)