mirror of
https://github.com/morluto/rea
synced 2026-10-26 14:25:49 -04:00
f4f7c75c66
Use the shared source owner for Mach-O and PyInstaller admission, descriptor and selected-path identity, hashing and cleanup. Retain lipo joining and reader-owned PyInstaller admission, caller cancellation and private PYZ leases. Centralize admission failure projection and remove the superseded PyInstaller wrapper. Preserve physical identity verification before late hash-completion cancellation. Close an owned source before returning failed initial metadata acquisition and retain the exact handle when close fails. A focused regression failed twice before that fix and now passes. Validation: fresh runtime build, 259 focused fixture tests, nine documentation checks and all local static/discovery/duplication gates passed. An earlier check also ran four system-lipo suite cases (three read-only native inspections) before the final source fix; those were excluded from subsequent fixture qualification.
438 lines
15 KiB
TypeScript
438 lines
15 KiB
TypeScript
import { createHash } from "node:crypto";
|
|
import {
|
|
open,
|
|
readFile,
|
|
rename,
|
|
writeFile,
|
|
type FileHandle,
|
|
} from "node:fs/promises";
|
|
import { dirname, join } from "node:path";
|
|
import { text } from "node:stream/consumers";
|
|
import { setImmediate } from "node:timers/promises";
|
|
import { afterEach, expect, it, vi } from "vitest";
|
|
|
|
import {
|
|
ArtifactReaderFailure,
|
|
type ArtifactEntry,
|
|
type ArtifactReader,
|
|
} from "../../src/artifacts/ArtifactReader.js";
|
|
import { ArtifactResourceScope } from "../../src/artifacts/ArtifactResourceScope.js";
|
|
import { GodotPckArtifactReader } from "../../src/artifacts/GodotPckArtifactReader.js";
|
|
import { NodeSeaArtifactReader } from "../../src/artifacts/NodeSeaArtifactReader.js";
|
|
import { nodeSeaElf } from "../../src/artifacts/nodeSea/NodeSea.fixture.js";
|
|
import type { StableRegularFileDescriptor } from "../../src/filesystem/RegularFile.js";
|
|
import { createDeferred } from "../fixtures/binarySession.js";
|
|
import { buildGodotPck } from "../fixtures/godotPck.js";
|
|
import { awaitAcknowledgement } from "../fixtures/lifecycleAcknowledgement.js";
|
|
import { createTestTempDirectory } from "../fixtures/temporaryDirectory.js";
|
|
|
|
interface OpenControl {
|
|
readonly paths: Set<string>;
|
|
readonly handles: Map<FileHandle, () => Promise<void>>;
|
|
onOpen: ((handle: FileHandle, path: string) => Promise<void>) | undefined;
|
|
}
|
|
|
|
const control = vi.hoisted((): OpenControl => ({
|
|
paths: new Set(),
|
|
handles: new Map(),
|
|
onOpen: undefined,
|
|
}));
|
|
|
|
vi.mock("node:fs/promises", async (importOriginal) => {
|
|
const original = await importOriginal<typeof import("node:fs/promises")>();
|
|
return {
|
|
...original,
|
|
open: async (...args: Parameters<typeof original.open>) => {
|
|
const handle = await original.open(...args);
|
|
const path = String(args[0]);
|
|
if (control.paths.has(path)) {
|
|
control.handles.set(handle, handle.close.bind(handle));
|
|
await control.onOpen?.(handle, path);
|
|
}
|
|
return handle;
|
|
},
|
|
};
|
|
});
|
|
|
|
afterEach(async () => {
|
|
control.onOpen = undefined;
|
|
for (const [handle, close] of control.handles)
|
|
if (handle.fd >= 0) await close();
|
|
control.handles.clear();
|
|
control.paths.clear();
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
const BODY = "export const artifact = 'retained';\n";
|
|
const carriers = [
|
|
{
|
|
name: "Godot PCK",
|
|
bytes: (body = BODY) => buildGodotPck(3, [{ path: "main.js", data: body }]),
|
|
reader: (path: string, admitted?: StableRegularFileDescriptor) =>
|
|
new GodotPckArtifactReader(path, admitted),
|
|
},
|
|
{
|
|
name: "Node SEA",
|
|
bytes: (body = BODY) => nodeSeaElf({ codePath: "main.js", main: body }),
|
|
reader: (path: string, admitted?: StableRegularFileDescriptor) =>
|
|
new NodeSeaArtifactReader(path, admitted),
|
|
},
|
|
] as const;
|
|
|
|
const carrierFile = async (carrier: (typeof carriers)[number]) => {
|
|
const root = await createTestTempDirectory("rea-artifact-source-lifecycle-");
|
|
const path = join(root, "carrier.bin");
|
|
const bytes = carrier.bytes();
|
|
await writeFile(path, bytes);
|
|
control.paths.add(path);
|
|
return { path, sha256: createHash("sha256").update(bytes).digest("hex") };
|
|
};
|
|
|
|
const entries = async (reader: ArtifactReader): Promise<ArtifactEntry[]> => {
|
|
const values: ArtifactEntry[] = [];
|
|
for await (const entry of reader.entries()) values.push(entry);
|
|
return values;
|
|
};
|
|
|
|
const openedHandle = (): FileHandle => {
|
|
const handles = [...control.handles.keys()];
|
|
expect(handles).toHaveLength(1);
|
|
const handle = handles[0];
|
|
if (handle === undefined) throw new Error("Missing admitted descriptor");
|
|
return handle;
|
|
};
|
|
|
|
it.each(carriers)(
|
|
"$name keeps the caller's expected digest and rechecks identity after binding",
|
|
async (carrier) => {
|
|
const { path, sha256 } = await carrierFile(carrier);
|
|
const reader = carrier.reader(path);
|
|
const expected = "0".repeat(64);
|
|
try {
|
|
await reader.prepareContainer(sha256);
|
|
await expect(reader.prepareContainer(expected)).rejects.toMatchObject({
|
|
reason: "integrity",
|
|
details: {
|
|
logicalPath: ".",
|
|
declaredSha256: expected,
|
|
calculatedSha256: sha256,
|
|
unpacked: false,
|
|
},
|
|
});
|
|
await reader.prepareContainer(sha256);
|
|
await writeFile(path, carrier.bytes(BODY.repeat(3)));
|
|
await expect(reader.prepareContainer(sha256)).rejects.toMatchObject({
|
|
reason: "integrity",
|
|
message: `${carrier.name} source identity changed: ${path}`,
|
|
});
|
|
} finally {
|
|
await reader.close();
|
|
}
|
|
},
|
|
);
|
|
|
|
it.each(carriers)(
|
|
"$name joins late admission before close and refuses replacement work",
|
|
async (carrier) => {
|
|
const { path, sha256 } = await carrierFile(carrier);
|
|
const admitted = createDeferred<void>();
|
|
const release = createDeferred<void>();
|
|
control.onOpen = async (handle) => {
|
|
vi.spyOn(handle, "close");
|
|
admitted.resolve();
|
|
await release.promise;
|
|
};
|
|
const reader = carrier.reader(path);
|
|
const operations: Promise<unknown>[] = [];
|
|
let closed = false;
|
|
try {
|
|
const preparing = reader.prepareContainer(sha256);
|
|
operations.push(preparing);
|
|
await awaitAcknowledgement(
|
|
admitted.promise,
|
|
preparing,
|
|
"carrier admission",
|
|
);
|
|
const handle = openedHandle();
|
|
const closeAttempts = [reader.close(), reader.close()];
|
|
operations.push(...closeAttempts);
|
|
const closing = Promise.all(closeAttempts).then(() => {
|
|
closed = true;
|
|
});
|
|
operations.push(closing);
|
|
await setImmediate();
|
|
expect(closed).toBe(false);
|
|
expect(handle.fd).toBeGreaterThanOrEqual(0);
|
|
expect(handle.close).not.toHaveBeenCalled();
|
|
release.resolve();
|
|
await preparing;
|
|
await closing;
|
|
expect(handle.fd).toBe(-1);
|
|
expect(handle.close).toHaveBeenCalledOnce();
|
|
await expect(reader.prepareContainer(sha256)).rejects.toMatchObject({
|
|
reason: "unavailable",
|
|
});
|
|
await expect(
|
|
reader.entries()[Symbol.asyncIterator]().next(),
|
|
).rejects.toMatchObject({
|
|
reason: "unavailable",
|
|
});
|
|
expect(control.handles.size).toBe(1);
|
|
await reader.close();
|
|
expect(handle.close).toHaveBeenCalledOnce();
|
|
} finally {
|
|
release.resolve();
|
|
await Promise.allSettled(operations);
|
|
await reader.close();
|
|
}
|
|
},
|
|
);
|
|
|
|
it.each(carriers)(
|
|
"$name retains a failed physical close and retries only that exact descriptor",
|
|
async (carrier) => {
|
|
const { path, sha256 } = await carrierFile(carrier);
|
|
const entered = createDeferred<void>();
|
|
const release = createDeferred<void>();
|
|
const failure = new Error("controlled carrier close failure");
|
|
let blocked = true;
|
|
control.onOpen = async (handle) => {
|
|
const close = handle.close.bind(handle);
|
|
vi.spyOn(handle, "close").mockImplementation(async () => {
|
|
entered.resolve();
|
|
await release.promise;
|
|
if (blocked) throw failure;
|
|
await close();
|
|
});
|
|
};
|
|
const reader = carrier.reader(path);
|
|
let closing: Promise<PromiseSettledResult<void>[]> | undefined;
|
|
try {
|
|
await reader.prepareContainer(sha256);
|
|
const [entry] = await entries(reader);
|
|
if (entry === undefined) throw new Error("Missing carrier member");
|
|
expect(await text(await reader.open(entry))).toBe(BODY);
|
|
const handle = openedHandle();
|
|
closing = Promise.allSettled([reader.close(), reader.close()]);
|
|
await awaitAcknowledgement(entered.promise, closing, "carrier close");
|
|
expect(handle.fd).toBeGreaterThanOrEqual(0);
|
|
expect(handle.close).toHaveBeenCalledOnce();
|
|
release.resolve();
|
|
expect(await closing).toEqual([
|
|
{ status: "rejected", reason: failure },
|
|
{ status: "rejected", reason: failure },
|
|
]);
|
|
expect(handle.fd).toBeGreaterThanOrEqual(0);
|
|
blocked = false;
|
|
await Promise.all([reader.close(), reader.close()]);
|
|
expect(handle.fd).toBe(-1);
|
|
expect(handle.close).toHaveBeenCalledTimes(2);
|
|
await reader.close();
|
|
expect(handle.close).toHaveBeenCalledTimes(2);
|
|
expect(control.handles.size).toBe(1);
|
|
} finally {
|
|
blocked = false;
|
|
release.resolve();
|
|
await closing;
|
|
await reader.close();
|
|
}
|
|
},
|
|
);
|
|
|
|
it.each(carriers)(
|
|
"$name borrows the admitted descriptor after path replacement and leaves its close to the caller",
|
|
async (carrier) => {
|
|
const { path, sha256 } = await carrierFile(carrier);
|
|
const handle = await open(path, "r");
|
|
const close = vi.spyOn(handle, "close");
|
|
let reader: ReturnType<typeof carrier.reader> | undefined;
|
|
try {
|
|
const replacement = join(dirname(path), "replacement.bin");
|
|
const replacementBytes = carrier.bytes("replacement source\n");
|
|
await writeFile(replacement, replacementBytes);
|
|
await rename(replacement, path);
|
|
reader = carrier.reader(path, {
|
|
handle,
|
|
initial: await handle.stat(),
|
|
});
|
|
await reader.prepareContainer(sha256);
|
|
const [entry] = await entries(reader);
|
|
if (entry === undefined)
|
|
throw new Error("Missing original carrier member");
|
|
expect(await text(await reader.open(entry))).toBe(BODY);
|
|
await reader.close();
|
|
await reader.close();
|
|
expect(close).not.toHaveBeenCalled();
|
|
expect(handle.fd).toBeGreaterThanOrEqual(0);
|
|
expect(await readFile(path)).toEqual(replacementBytes);
|
|
expect(control.handles.size).toBe(1);
|
|
await handle.close();
|
|
expect(close).toHaveBeenCalledOnce();
|
|
expect(handle.fd).toBe(-1);
|
|
await reader.close();
|
|
expect(close).toHaveBeenCalledOnce();
|
|
} finally {
|
|
try {
|
|
await reader?.close();
|
|
} finally {
|
|
if (handle.fd >= 0) await handle.close();
|
|
}
|
|
}
|
|
},
|
|
);
|
|
|
|
const admissionCases = carriers.flatMap((carrier) =>
|
|
(
|
|
[
|
|
{ primary: "io", closeFails: false },
|
|
{ primary: "io", closeFails: true },
|
|
{ primary: "cancelled", closeFails: false },
|
|
{ primary: "cancelled", closeFails: true },
|
|
] as const
|
|
).map((scenario) => ({ name: carrier.name, carrier, ...scenario })),
|
|
);
|
|
|
|
it.each(admissionCases)(
|
|
"$name awaits physical admission cleanup and preserves $primary (closeFails=$closeFails)",
|
|
async ({ carrier, primary, closeFails }) => {
|
|
const { path } = await carrierFile(carrier);
|
|
const entered = createDeferred<void>();
|
|
const release = createDeferred<void>();
|
|
const controller = new AbortController();
|
|
const admissionFailure = new Error("controlled descriptor stat failure");
|
|
const cleanupFailure = new Error("controlled admission close failure");
|
|
const cancellation = { request: "cancelled carrier admission" };
|
|
let blocked = closeFails;
|
|
control.onOpen = async (handle) => {
|
|
if (primary === "io")
|
|
vi.spyOn(handle, "stat").mockRejectedValueOnce(admissionFailure);
|
|
else controller.abort(cancellation);
|
|
const close = handle.close.bind(handle);
|
|
vi.spyOn(handle, "close").mockImplementation(async () => {
|
|
entered.resolve();
|
|
await release.promise;
|
|
if (blocked) throw cleanupFailure;
|
|
await close();
|
|
});
|
|
};
|
|
const reader = carrier.reader(path);
|
|
let settled = false;
|
|
let reading: Promise<unknown> | undefined;
|
|
try {
|
|
const operation = reader
|
|
.entries(controller.signal)
|
|
[Symbol.asyncIterator]()
|
|
.next();
|
|
reading = operation
|
|
.catch((cause: unknown) => cause)
|
|
.finally(() => {
|
|
settled = true;
|
|
});
|
|
await awaitAcknowledgement(
|
|
entered.promise,
|
|
operation,
|
|
"admission cleanup",
|
|
);
|
|
const handle = openedHandle();
|
|
// An earlier metadata failure stays primary when cancellation arrives in close.
|
|
controller.abort(cancellation);
|
|
await setImmediate();
|
|
expect(settled).toBe(false);
|
|
expect(handle.fd).toBeGreaterThanOrEqual(0);
|
|
release.resolve();
|
|
const failure = await reading;
|
|
if (!(failure instanceof ArtifactReaderFailure))
|
|
throw new Error("Expected a typed carrier admission failure");
|
|
expect(failure.reason).toBe(primary);
|
|
const admission = closeFails ? failure.cause : failure;
|
|
if (!(admission instanceof ArtifactReaderFailure))
|
|
throw new Error("Missing the primary admission failure");
|
|
expect(admission.cause).toBe(
|
|
primary === "io" ? admissionFailure : cancellation,
|
|
);
|
|
expect(failure.cancellation).toEqual(
|
|
primary === "cancelled" ? { reason: cancellation } : undefined,
|
|
);
|
|
if (closeFails) {
|
|
expect(failure.cleanup).toEqual({
|
|
reason: cleanupFailure.message,
|
|
resources: [path],
|
|
});
|
|
expect(handle.fd).toBeGreaterThanOrEqual(0);
|
|
await expect(reader.close()).rejects.toBe(cleanupFailure);
|
|
expect(handle.close).toHaveBeenCalledTimes(2);
|
|
blocked = false;
|
|
await reader.close();
|
|
expect(handle.close).toHaveBeenCalledTimes(3);
|
|
} else {
|
|
expect(failure.cleanup).toBeUndefined();
|
|
expect(handle.fd).toBe(-1);
|
|
await reader.close();
|
|
expect(handle.close).toHaveBeenCalledOnce();
|
|
}
|
|
expect(handle.fd).toBe(-1);
|
|
expect(control.handles.size).toBe(1);
|
|
} finally {
|
|
controller.abort(cancellation);
|
|
blocked = false;
|
|
release.resolve();
|
|
await reading;
|
|
await reader.close();
|
|
}
|
|
},
|
|
);
|
|
|
|
it("retries a failed reader without closing a successfully released peer again", async () => {
|
|
const good = await carrierFile(carriers[0]);
|
|
const failed = await carrierFile(carriers[1]);
|
|
const cleanupFailure = new Error("controlled Node SEA peer close failure");
|
|
let blocked = true;
|
|
control.onOpen = async (handle, path) => {
|
|
const close = handle.close.bind(handle);
|
|
vi.spyOn(handle, "close").mockImplementation(async () => {
|
|
if (path === failed.path && blocked) throw cleanupFailure;
|
|
await close();
|
|
});
|
|
};
|
|
const goodReader = carriers[0].reader(good.path);
|
|
const failedReader = carriers[1].reader(failed.path);
|
|
const scope = new ArtifactResourceScope();
|
|
try {
|
|
await goodReader.prepareContainer(good.sha256);
|
|
await failedReader.prepareContainer(failed.sha256);
|
|
const handles = [...control.handles.keys()];
|
|
const [goodHandle, failedHandle] = handles;
|
|
if (goodHandle === undefined || failedHandle === undefined)
|
|
throw new Error("Missing the two exact admitted descriptors");
|
|
expect(
|
|
await scope.release({
|
|
kind: "reader",
|
|
resource: good.path,
|
|
reader: goodReader,
|
|
}),
|
|
).toEqual({ kind: "released" });
|
|
const release = await scope.release({
|
|
kind: "reader",
|
|
resource: failed.path,
|
|
reader: failedReader,
|
|
});
|
|
expect(release).toMatchObject({ kind: "failed", cause: cleanupFailure });
|
|
expect(goodHandle.fd).toBe(-1);
|
|
expect(failedHandle.fd).toBeGreaterThanOrEqual(0);
|
|
expect(scope.cleanupResources).toEqual([failed.path]);
|
|
blocked = false;
|
|
await Promise.all([scope.close(), scope.close()]);
|
|
await scope.close();
|
|
expect(goodHandle.close).toHaveBeenCalledOnce();
|
|
expect(failedHandle.close).toHaveBeenCalledTimes(2);
|
|
expect(failedHandle.fd).toBe(-1);
|
|
expect(scope.cleanupResources).toEqual([]);
|
|
expect(control.handles.size).toBe(2);
|
|
} finally {
|
|
blocked = false;
|
|
await scope.close();
|
|
await goodReader.close();
|
|
await failedReader.close();
|
|
}
|
|
});
|