mirror of
https://github.com/morluto/rea
synced 2026-10-26 06:16:17 -04:00
d5df7fd944
Share parser-owned package and runtime facts across dependency consumers. Preserve uncertain loader requests and explicit wrapper bindings without inventing unbound require authority in known modules. Cover selected source grammar through the public MCP boundary and retain ambient/type dependencies. Open each selected directory before loading its scoped policy, retaining partial directory failures and cleanup ownership. Preserve explicit caller negation precedence and source diagnostics across inventory projections. Clarify how version comparison retains container provenance without claiming semantic drift. Use independent unknown source inputs to keep candidate-limit fixtures meaningful as conditional branch evaluation becomes more precise. Fixes #6891 Fixes #6882 Fixes #6883 Fixes #6859
405 lines
12 KiB
TypeScript
405 lines
12 KiB
TypeScript
import {
|
|
mkdir,
|
|
open as fsOpen,
|
|
opendir as fsOpendir,
|
|
writeFile,
|
|
type FileHandle,
|
|
} from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
|
|
import { afterEach, beforeEach, expect, it, onTestFinished, vi } from "vitest";
|
|
|
|
import { ArtifactResourceScope } from "../../../src/artifacts/ArtifactResourceScope.js";
|
|
import { importReferenceSource } from "../../../src/application/ReferenceSourceImport.js";
|
|
import { readReferenceSource } from "../../../src/reference/ReferenceSourceReader.js";
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
import { err, ok } from "../../../src/domain/result.js";
|
|
|
|
vi.mock("node:fs/promises", async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import("node:fs/promises")>();
|
|
return {
|
|
...actual,
|
|
open: vi.fn(actual.open),
|
|
opendir: vi.fn(actual.opendir),
|
|
};
|
|
});
|
|
|
|
const openMock = vi.mocked(fsOpen);
|
|
const opendirMock = vi.mocked(fsOpendir);
|
|
let actual: typeof import("node:fs/promises");
|
|
|
|
beforeEach(async () => {
|
|
actual =
|
|
await vi.importActual<typeof import("node:fs/promises")>(
|
|
"node:fs/promises",
|
|
);
|
|
openMock.mockImplementation((...args) => actual.open(...args));
|
|
opendirMock.mockImplementation((...args) => actual.opendir(...args));
|
|
});
|
|
|
|
afterEach(() => {
|
|
openMock.mockReset();
|
|
opendirMock.mockReset();
|
|
});
|
|
|
|
it.each([
|
|
["refusal", false],
|
|
["refusal", true],
|
|
["cancel-after-policy", false],
|
|
["cancel-after-policy", true],
|
|
["refusal-late-cancel", true],
|
|
] as const)(
|
|
"owns directory cleanup before policy %s (close fails=%s)",
|
|
async (outcome, closeFails) => {
|
|
const root = await createTestTempDirectory("rea-reference-policy-owner-");
|
|
await writeFile(join(root, "unexamined.ts"), "export const value = 1;\n");
|
|
const resources = new ArtifactResourceScope();
|
|
onTestFinished(() => resources.close().catch(() => undefined));
|
|
const controller = new AbortController();
|
|
const reason = new Error("cancelled at the policy boundary");
|
|
const policyCause = new Error("scoped policy read failed");
|
|
const closeFailure = new Error("directory close rejected after policy");
|
|
let closeCalls = 0;
|
|
let readCalls = 0;
|
|
opendirMock.mockImplementation(async (...args) => {
|
|
const directory = await actual.opendir(...args);
|
|
const read = directory.read.bind(directory);
|
|
vi.spyOn(directory, "read").mockImplementation(async () => {
|
|
readCalls += 1;
|
|
return read();
|
|
});
|
|
const close = directory.close.bind(directory);
|
|
vi.spyOn(directory, "close").mockImplementation(async () => {
|
|
closeCalls += 1;
|
|
if (outcome === "refusal-late-cancel") controller.abort(reason);
|
|
await close();
|
|
if (closeFails) throw closeFailure;
|
|
});
|
|
return directory;
|
|
});
|
|
const result = await readReferenceSource(root, resources, {
|
|
signal: controller.signal,
|
|
beforeDirectory: async () => {
|
|
expect(opendirMock).toHaveBeenCalledTimes(1);
|
|
if (outcome === "cancel-after-policy") {
|
|
controller.abort(reason);
|
|
return ok(undefined);
|
|
}
|
|
return err({
|
|
tag: "reference-source-reader",
|
|
code: "io",
|
|
message: "Scoped policy could not be read",
|
|
cause: policyCause,
|
|
});
|
|
},
|
|
});
|
|
if (result.ok) throw new Error("Policy outcome was ignored");
|
|
expect(result.error.code).toBe(
|
|
outcome === "cancel-after-policy" ? "cancelled" : "io",
|
|
);
|
|
if (outcome !== "cancel-after-policy") {
|
|
expect(result.error.message).toBe("Scoped policy could not be read");
|
|
expect(result.error.cause).toBe(policyCause);
|
|
}
|
|
expect(result.error.partial).toMatchObject({ entries: [], bytesRead: 0 });
|
|
expect(readCalls).toBe(0);
|
|
expect(openMock).not.toHaveBeenCalled();
|
|
expect(closeCalls).toBe(1);
|
|
expect(resources.hasPending).toBe(closeFails);
|
|
if (closeFails) {
|
|
expect(result.error.cleanup).toEqual({
|
|
resources: [root],
|
|
reason: expect.stringContaining(closeFailure.message),
|
|
});
|
|
await expect(resources.close()).rejects.toMatchObject({
|
|
cleanup: { resources: [root] },
|
|
});
|
|
} else {
|
|
expect(result.error.cleanup).toBeUndefined();
|
|
await resources.close();
|
|
}
|
|
expect(closeCalls).toBe(1);
|
|
},
|
|
);
|
|
|
|
it("preserves admission EIO and retains an invalidated failed-close owner", async () => {
|
|
const root = await createTestTempDirectory("rea-reference-admission-owner-");
|
|
const file = join(root, "source.ts");
|
|
await writeFile(file, "export const answer = 42;\n");
|
|
const resources = new ArtifactResourceScope();
|
|
onTestFinished(() => resources.close().catch(() => undefined));
|
|
const statFailure = Object.assign(new Error("injected admission EIO"), {
|
|
code: "EIO",
|
|
});
|
|
const closeFailure = new Error("native close rejected after invalidation");
|
|
let closeCalls = 0;
|
|
|
|
openMock.mockImplementation(async (...args) => {
|
|
const handle = await actual.open(...args);
|
|
vi.spyOn(handle, "stat").mockRejectedValueOnce(statFailure);
|
|
const close = handle.close.bind(handle);
|
|
vi.spyOn(handle, "close").mockImplementation(async () => {
|
|
closeCalls += 1;
|
|
await close();
|
|
throw closeFailure;
|
|
});
|
|
return handle;
|
|
});
|
|
|
|
const result = await readReferenceSource(root, resources);
|
|
expect(result).toMatchObject({
|
|
ok: false,
|
|
error: {
|
|
tag: "reference-source-reader",
|
|
code: "io",
|
|
message: expect.stringContaining("EIO"),
|
|
cleanup: {
|
|
resources: [file],
|
|
reason: expect.stringContaining(closeFailure.message),
|
|
},
|
|
partial: {
|
|
entries: [
|
|
{
|
|
status: "failed",
|
|
kind: "file",
|
|
path: "source.ts",
|
|
code: "io",
|
|
message: expect.stringContaining("EIO"),
|
|
},
|
|
],
|
|
},
|
|
},
|
|
});
|
|
expect(closeCalls).toBe(1);
|
|
|
|
await expect(resources.close()).rejects.toMatchObject({
|
|
cleanup: {
|
|
resources: [file],
|
|
reason: expect.stringContaining(closeFailure.message),
|
|
},
|
|
});
|
|
expect(closeCalls).toBe(1);
|
|
});
|
|
|
|
const assertSourceCleanupRetry = async (enclosingGit: boolean) => {
|
|
const workspace = await createTestTempDirectory("rea-reference-file-owner-");
|
|
const root = join(workspace, "source");
|
|
await mkdir(root);
|
|
if (enclosingGit) {
|
|
const gitDirectory = join(workspace, ".git");
|
|
await mkdir(gitDirectory);
|
|
await writeFile(
|
|
join(gitDirectory, "HEAD"),
|
|
"0123456789abcdef0123456789abcdef01234567\n",
|
|
);
|
|
}
|
|
const file = join(root, "source.ts");
|
|
const bytes = "export const answer = 'retained';\n";
|
|
await writeFile(file, bytes);
|
|
const resources = new ArtifactResourceScope();
|
|
const closeFailure = new Error("injected close before native close");
|
|
let allowClose = false;
|
|
onTestFinished(async () => {
|
|
allowClose = true;
|
|
await resources.close().catch(() => undefined);
|
|
});
|
|
let closeCalls = 0;
|
|
let openCalls = 0;
|
|
let sourceHandle: FileHandle | undefined;
|
|
|
|
openMock.mockImplementation(async (...args) => {
|
|
if (args[0] !== file) return actual.open(...args);
|
|
openCalls += 1;
|
|
const handle = await actual.open(...args);
|
|
sourceHandle = handle;
|
|
const close = handle.close.bind(handle);
|
|
vi.spyOn(handle, "close").mockImplementation(async () => {
|
|
closeCalls += 1;
|
|
if (!allowClose) throw closeFailure;
|
|
await close();
|
|
});
|
|
return handle;
|
|
});
|
|
|
|
const first = await importReferenceSource(
|
|
{
|
|
root,
|
|
caller: "reference-source-cleanup-owner-test",
|
|
policy: { secretPatterns: [] },
|
|
},
|
|
resources,
|
|
);
|
|
expect(first).toMatchObject({
|
|
ok: false,
|
|
error: {
|
|
code: "io",
|
|
cleanup: {
|
|
resources: [file],
|
|
reason: expect.stringContaining(closeFailure.message),
|
|
},
|
|
partial: {
|
|
entries: [
|
|
{
|
|
status: "read",
|
|
kind: "file",
|
|
path: "source.ts",
|
|
bytes: Buffer.from(bytes),
|
|
},
|
|
],
|
|
bytesRead: Buffer.byteLength(bytes),
|
|
},
|
|
},
|
|
});
|
|
expect(openCalls).toBe(1);
|
|
expect(closeCalls).toBe(1);
|
|
expect(sourceHandle?.fd).toBeGreaterThanOrEqual(0);
|
|
|
|
const blocked = await readReferenceSource(root, resources);
|
|
expect(blocked).toMatchObject({
|
|
ok: false,
|
|
error: {
|
|
code: "io",
|
|
cleanup: { resources: [file] },
|
|
},
|
|
});
|
|
expect(openCalls).toBe(1);
|
|
expect(closeCalls).toBe(2);
|
|
expect(sourceHandle?.fd).toBeGreaterThanOrEqual(0);
|
|
|
|
allowClose = true;
|
|
await resources.close();
|
|
expect(openCalls).toBe(1);
|
|
expect(closeCalls).toBe(3);
|
|
expect(sourceHandle?.fd).toBe(-1);
|
|
};
|
|
|
|
it.each([false, true])(
|
|
"retains partial file bytes and retries the same live owner before new work (enclosing Git repository: %s)",
|
|
assertSourceCleanupRetry,
|
|
);
|
|
|
|
it("preserves a directory read failure when closing that directory also fails", async () => {
|
|
const root = await createTestTempDirectory(
|
|
"rea-reference-directory-read-owner-",
|
|
);
|
|
const file = join(root, "source.ts");
|
|
const bytes = "observed before directory EIO\n";
|
|
await writeFile(file, bytes);
|
|
const resources = new ArtifactResourceScope();
|
|
onTestFinished(() => resources.close().catch(() => undefined));
|
|
const readFailure = Object.assign(new Error("injected directory EIO"), {
|
|
code: "EIO",
|
|
});
|
|
const closeFailure = new Error("directory close rejected after invalidation");
|
|
let readCalls = 0;
|
|
let closeCalls = 0;
|
|
|
|
opendirMock.mockImplementation(async (...args) => {
|
|
const directory = await actual.opendir(...args);
|
|
const read = directory.read.bind(directory);
|
|
vi.spyOn(directory, "read").mockImplementation(async () => {
|
|
if (readCalls++ > 0) throw readFailure;
|
|
return read();
|
|
});
|
|
const close = directory.close.bind(directory);
|
|
vi.spyOn(directory, "close").mockImplementation(async () => {
|
|
closeCalls += 1;
|
|
await close();
|
|
throw closeFailure;
|
|
});
|
|
return directory;
|
|
});
|
|
|
|
const result = await readReferenceSource(root, resources);
|
|
if (result.ok) throw new Error("Directory and cleanup failures were ignored");
|
|
expect(result).toMatchObject({
|
|
ok: false,
|
|
error: {
|
|
code: "io",
|
|
message: expect.stringContaining(readFailure.message),
|
|
cleanup: {
|
|
resources: [root],
|
|
reason: expect.stringContaining(closeFailure.message),
|
|
},
|
|
partial: {
|
|
bytesRead: Buffer.byteLength(bytes),
|
|
},
|
|
},
|
|
});
|
|
expect(result.error.partial?.entries).toContainEqual(
|
|
expect.objectContaining({
|
|
status: "read",
|
|
kind: "file",
|
|
path: "source.ts",
|
|
bytes: Buffer.from(bytes),
|
|
}),
|
|
);
|
|
expect(result.error.partial?.rootFailure).toEqual({
|
|
code: "io",
|
|
message: expect.stringContaining(readFailure.message),
|
|
});
|
|
expect(closeCalls).toBe(1);
|
|
|
|
await expect(resources.close()).rejects.toMatchObject({
|
|
cleanup: {
|
|
resources: [root],
|
|
reason: expect.stringContaining(closeFailure.message),
|
|
},
|
|
});
|
|
expect(closeCalls).toBe(1);
|
|
});
|
|
|
|
it("keeps cancelled traversal typed when native directory close rejects after invalidation", async () => {
|
|
const root = await createTestTempDirectory("rea-reference-directory-owner-");
|
|
await mkdir(join(root, "child"));
|
|
const resources = new ArtifactResourceScope();
|
|
onTestFinished(() => resources.close().catch(() => undefined));
|
|
const controller = new AbortController();
|
|
const closeFailure = new Error("native directory close rejected");
|
|
let closeCalls = 0;
|
|
let openedPath: string | undefined;
|
|
|
|
opendirMock.mockImplementation(async (...args) => {
|
|
const directory = await actual.opendir(...args);
|
|
openedPath = String(args[0]);
|
|
const read = directory.read.bind(directory);
|
|
vi.spyOn(directory, "read").mockImplementation(async () => {
|
|
const child = await read();
|
|
if (child !== null) controller.abort();
|
|
return child;
|
|
});
|
|
const close = directory.close.bind(directory);
|
|
vi.spyOn(directory, "close").mockImplementation(async () => {
|
|
closeCalls += 1;
|
|
await close();
|
|
throw closeFailure;
|
|
});
|
|
return directory;
|
|
});
|
|
|
|
const result = await readReferenceSource(root, resources, {
|
|
signal: controller.signal,
|
|
});
|
|
expect(result).toMatchObject({
|
|
ok: false,
|
|
error: {
|
|
code: "cancelled",
|
|
cleanup: {
|
|
resources: [root],
|
|
reason: expect.stringContaining(closeFailure.message),
|
|
},
|
|
partial: { entries: [] },
|
|
},
|
|
});
|
|
expect(openedPath).toBe(root);
|
|
expect(closeCalls).toBe(1);
|
|
|
|
await expect(resources.close()).rejects.toMatchObject({
|
|
cleanup: {
|
|
resources: [root],
|
|
reason: expect.stringContaining(closeFailure.message),
|
|
},
|
|
});
|
|
expect(closeCalls).toBe(1);
|
|
});
|