mirror of
https://github.com/morluto/rea
synced 2026-10-27 22:45:19 -04:00
baad9eebe4
* fix(javascript): reject FIFO recovery inputs without blocking scanRecoveryFile opened the selected path with a blocking O_RDONLY open before checking that it is a regular file. A FIFO with no writer blocks that open in the libuv threadpool, so cancellation and the recovery deadline could not settle the read, and the process could not exit. Admit the input through the shared openRegularFile owner, which opens with O_NONBLOCK and verifies a regular file before any read. A FIFO, directory or socket now fails admission immediately with the existing regular-file error; symlink rejection and cancellation are unchanged. Fixes #3332 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(javascript): keep a failed admission close owned on cancellation Rethrow RegularFileAdmissionFailure before mapping cancellation, so a descriptor whose close failed stays owned by its error. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
56 lines
2.2 KiB
TypeScript
56 lines
2.2 KiB
TypeScript
import { execFile } from "node:child_process";
|
|
import { writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { promisify } from "node:util";
|
|
import { expect, it, vi } from "vitest";
|
|
import { AnalysisOutputError } from "../../domain/analysisErrorCore.js";
|
|
import { OwnedFileHandle } from "../../filesystem/OwnedFileHandle.js";
|
|
import {
|
|
openRegularFile,
|
|
RegularFileAdmissionFailure,
|
|
} from "../../filesystem/RegularFile.js";
|
|
import { readWithoutFifoWriter } from "../../../tests/fixtures/fifoInput.js";
|
|
import { createTestTempDirectory } from "../../../tests/fixtures/temporaryDirectory.js";
|
|
import { readRecoveryFile } from "./RecoveryFiles.js";
|
|
|
|
vi.mock("../../filesystem/RegularFile.js", async (importOriginal) => {
|
|
const actual =
|
|
await importOriginal<typeof import("../../filesystem/RegularFile.js")>();
|
|
return { ...actual, openRegularFile: vi.fn(actual.openRegularFile) };
|
|
});
|
|
|
|
it.skipIf(process.platform === "win32")(
|
|
"rejects a named pipe without waiting for a writer",
|
|
async () => {
|
|
const root = await createTestTempDirectory("rea-recovery-fifo-");
|
|
const regular = join(root, "regular.js");
|
|
await writeFile(regular, "module.exports = 1;\n");
|
|
await expect(readRecoveryFile(regular, 1024)).resolves.toHaveLength(20);
|
|
|
|
const fifoPath = join(root, "sample.js");
|
|
await promisify(execFile)("mkfifo", [fifoPath]);
|
|
const outcome = await readWithoutFifoWriter(fifoPath, () =>
|
|
readRecoveryFile(fifoPath, 1024).catch((cause: unknown) => cause),
|
|
);
|
|
if (outcome.state !== "completed")
|
|
throw new Error("Recovery input read waited for a FIFO writer");
|
|
expect(outcome.result).toBeInstanceOf(AnalysisOutputError);
|
|
},
|
|
);
|
|
|
|
it("keeps a failed admission close owned when cancellation is also requested", async () => {
|
|
const owner = new OwnedFileHandle({ fd: -1 } as never);
|
|
const failure = new RegularFileAdmissionFailure(
|
|
"/selected.js",
|
|
new Error("cancelled during admission"),
|
|
new Error("close failed"),
|
|
owner,
|
|
);
|
|
vi.mocked(openRegularFile).mockRejectedValueOnce(failure);
|
|
const controller = new AbortController();
|
|
controller.abort();
|
|
await expect(
|
|
readRecoveryFile("/selected.js", 1024, controller.signal),
|
|
).rejects.toBe(failure);
|
|
});
|