76 lines
2.9 KiB
TypeScript
76 lines
2.9 KiB
TypeScript
import { writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
|
|
import { expect, it, vi } from "vitest";
|
|
|
|
import { ArtifactResourceScope } from "../../../src/artifacts/ArtifactResourceScope.js";
|
|
import { readJsonFile } from "../../../src/application/JsonFiles.js";
|
|
import { RegularFileCleanupFailure } from "../../../src/application/RegularFileRead.js";
|
|
import { OwnedFileHandle } from "../../../src/filesystem/OwnedFileHandle.js";
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
|
|
it("retains and retries a JSON file owner after a persistent close failure", async () => {
|
|
const root = await createTestTempDirectory("rea-json-file-owner-");
|
|
const path = join(root, "input.json");
|
|
await writeFile(path, '{"value":"retained"}');
|
|
const resources = new ArtifactResourceScope();
|
|
const closeFailure = new Error("injected persistent close failure");
|
|
let attempts = 0;
|
|
const close = vi
|
|
.spyOn(OwnedFileHandle.prototype, "close")
|
|
.mockImplementation(async () => {
|
|
attempts += 1;
|
|
throw closeFailure;
|
|
});
|
|
let failure: unknown;
|
|
try {
|
|
try {
|
|
await readJsonFile(path, undefined, resources);
|
|
} catch (cause: unknown) {
|
|
failure = cause;
|
|
}
|
|
expect(failure).toBeInstanceOf(RegularFileCleanupFailure);
|
|
if (!(failure instanceof RegularFileCleanupFailure))
|
|
throw new Error("Expected the failed file owner to remain visible");
|
|
expect(failure.cleanupCause).toBe(closeFailure);
|
|
expect(failure.outcome).toMatchObject({
|
|
kind: "completed",
|
|
value: { ok: true, value: { value: "retained" } },
|
|
});
|
|
expect(failure.owner.handle.fd).toBeGreaterThanOrEqual(0);
|
|
expect(attempts).toBe(2);
|
|
} finally {
|
|
close.mockRestore();
|
|
await resources.close();
|
|
}
|
|
if (!(failure instanceof RegularFileCleanupFailure))
|
|
throw new Error("Expected the retained file owner");
|
|
expect(failure.owner.handle.fd).toBe(-1);
|
|
});
|
|
|
|
it("returns parsed JSON when a failed file close is recovered by the resource scope", async () => {
|
|
const root = await createTestTempDirectory("rea-json-file-close-retry-");
|
|
const path = join(root, "input.json");
|
|
await writeFile(path, '{"value":"retained"}');
|
|
const resources = new ArtifactResourceScope();
|
|
const closeFailure = new Error("injected transient close failure");
|
|
const originalClose = OwnedFileHandle.prototype.close;
|
|
let attempts = 0;
|
|
const close = vi
|
|
.spyOn(OwnedFileHandle.prototype, "close")
|
|
.mockImplementation(async function (this: OwnedFileHandle) {
|
|
attempts += 1;
|
|
if (attempts === 1) throw closeFailure;
|
|
await originalClose.call(this);
|
|
});
|
|
try {
|
|
const parsed = await readJsonFile(path, undefined, resources);
|
|
if (!parsed.ok) throw parsed.error;
|
|
expect(parsed.value).toEqual({ value: "retained" });
|
|
expect(attempts).toBe(2);
|
|
} finally {
|
|
close.mockRestore();
|
|
await resources.close();
|
|
}
|
|
});
|