blob: 15a2aebaa45e2f85b518ba17be551c8e19d0e243 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import assert from 'node:assert/strict';
import { mkdtemp, readFile, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { test } from 'node:test';
import { runFilesystemWorkerProcess } from '../filesystem-worker/process-runner.js';
test('filesystem worker process receives inherited fd inputs alongside request stdin', async () => {
const fdPayload = Uint8Array.from([1, 2, 3, 4]);
const script = [
"const fs = require('node:fs');",
'const fd = [...fs.readFileSync(3)];',
"let stdin = '';",
"process.stdin.setEncoding('utf8');",
"process.stdin.on('data', (chunk) => { stdin += chunk; });",
"process.stdin.on('end', () => process.stdout.write(JSON.stringify({ fd, stdin })));",
].join('');
const input = {
argv: [process.execPath, '-e', script],
cwd: tmpdir(),
env: process.env,
stdin: '{"request":true}',
fdInputs: [{ fd: 3, data: fdPayload }],
};
const result = await runFilesystemWorkerProcess(input);
assert.equal(result.exitCode, 0, result.stderrTail);
assert.deepEqual(JSON.parse(result.stdout), {
fd: [...fdPayload],
stdin: input.stdin,
});
});
test('filesystem worker does not spawn for an already-aborted request', async () => {
const root = await mkdtemp(join(tmpdir(), 'maka-worker-pre-abort-'));
const marker = join(root, 'spawned');
const abort = new AbortController();
abort.abort();
try {
const result = await runFilesystemWorkerProcess({
argv: [
process.execPath,
'-e',
`require('node:fs').writeFileSync(${JSON.stringify(marker)}, 'spawned')`,
],
cwd: root,
env: process.env,
stdin: '',
abortSignal: abort.signal,
});
assert.equal(result.aborted, true);
await assert.rejects(() => readFile(marker, 'utf8'), { code: 'ENOENT' });
} finally {
await rm(root, { recursive: true, force: true });
}
});
test('filesystem worker rejects boundedly when a detached descendant retains stdout', {
skip: process.platform === 'win32' ? 'POSIX detached process-group semantics required' : false,
}, async () => {
const root = await mkdtemp(join(tmpdir(), 'maka-worker-drain-'));
const pidFile = join(root, 'child.pid');
let childPid: number | undefined;
try {
const script = `const {spawn}=require('node:child_process');const {writeFileSync}=require('node:fs');const child=spawn(process.execPath,['-e','setInterval(()=>{},1000)'],{detached:true,stdio:['ignore',process.stdout,'ignore']});child.unref();writeFileSync(${JSON.stringify(pidFile)},String(child.pid));process.stdout.write('{}')`;
const startedAt = Date.now();
await assert.rejects(
runFilesystemWorkerProcess({
argv: [process.execPath, '-e', script],
cwd: root,
env: process.env,
stdin: '',
ioDrainTimeoutMs: 100,
}),
/output did not drain before lifecycle deadline/,
);
childPid = Number.parseInt(await readFile(pidFile, 'utf8'), 10);
assert.ok(Date.now() - startedAt < 2_000);
} finally {
if (childPid) {
try {
process.kill(-childPid, 'SIGKILL');
} catch {
try {
process.kill(childPid, 'SIGKILL');
} catch {
/* descendant already exited */
}
}
}
await rm(root, { recursive: true, force: true });
}
});