import path from "node:path" ;
import { beforeEach, describe, expect, it, vi } from "vitest" ;
const resolveBoundaryPathSyncMock = vi.hoisted(() => vi.fn());
const resolveBoundaryPathMock = vi.hoisted(() => vi.fn());
const openVerifiedFileSyncMock = vi.hoisted(() => vi.fn());
vi.mock("./boundary-path.js" , () => ({
resolveBoundaryPathSync: (...args: unknown[]) => resolveBoundaryPathSyncMock(...args),
resolveBoundaryPath: (...args: unknown[]) => resolveBoundaryPathMock(...args),
}));
vi.mock("./safe-open-sync.js" , () => ({
openVerifiedFileSync: (...args: unknown[]) => openVerifiedFileSyncMock(...args),
}));
let canUseBoundaryFileOpen: typeof import ("./boundary-file-read.js" ).canUseBoundaryFileOpen;
let matchBoundaryFileOpenFailure: typeof import ("./boundary-file-read.js" ).matchBoundaryFileOpenFailure;
let openBoundaryFile: typeof import ("./boundary-file-read.js" ).openBoundaryFile;
let openBoundaryFileSync: typeof import ("./boundary-file-read.js" ).openBoundaryFileSync;
describe("boundary-file-read" , () => {
beforeEach(async () => {
vi.resetModules();
({
canUseBoundaryFileOpen,
matchBoundaryFileOpenFailure,
openBoundaryFile,
openBoundaryFileSync,
} = await import ("./boundary-file-read.js" ));
resolveBoundaryPathSyncMock.mockReset();
resolveBoundaryPathMock.mockReset();
openVerifiedFileSyncMock.mockReset();
});
it("recognizes the required sync fs surface" , () => {
const validFs = {
openSync() {},
closeSync() {},
fstatSync() {},
lstatSync() {},
realpathSync() {},
readFileSync() {},
constants: {},
};
expect(canUseBoundaryFileOpen(validFs as never)).toBe(true );
expect(
canUseBoundaryFileOpen({
...validFs,
openSync: undefined,
} as never),
).toBe(false );
expect(
canUseBoundaryFileOpen({
...validFs,
constants: null ,
} as never),
).toBe(false );
});
it("maps sync boundary resolution into verified file opens" , () => {
const stat = { size: 3 } as never;
const ioFs = { marker: "io" } as never;
const absolutePath = path.resolve("plugin.json" );
resolveBoundaryPathSyncMock.mockReturnValue({
canonicalPath: "/real/plugin.json" ,
rootCanonicalPath: "/real/root" ,
});
openVerifiedFileSyncMock.mockReturnValue({
ok: true ,
path: "/real/plugin.json" ,
fd: 7 ,
stat,
});
const opened = openBoundaryFileSync({
absolutePath: "plugin.json" ,
rootPath: "/workspace" ,
boundaryLabel: "plugin root" ,
ioFs,
});
expect(resolveBoundaryPathSyncMock).toHaveBeenCalledWith({
absolutePath,
rootPath: "/workspace" ,
rootCanonicalPath: undefined,
boundaryLabel: "plugin root" ,
skipLexicalRootCheck: undefined,
});
expect(openVerifiedFileSyncMock).toHaveBeenCalledWith({
filePath: absolutePath,
resolvedPath: "/real/plugin.json" ,
rejectHardlinks: true ,
maxBytes: undefined,
allowedType: undefined,
ioFs,
});
expect(opened).toEqual({
ok: true ,
path: "/real/plugin.json" ,
fd: 7 ,
stat,
rootRealPath: "/real/root" ,
});
});
it("returns validation errors when sync boundary resolution throws" , () => {
const error = new Error("outside root" );
resolveBoundaryPathSyncMock.mockImplementation(() => {
throw error;
});
const opened = openBoundaryFileSync({
absolutePath: "plugin.json" ,
rootPath: "/workspace" ,
boundaryLabel: "plugin root" ,
});
expect(opened).toEqual({
ok: false ,
reason: "validation" ,
error,
});
expect(openVerifiedFileSyncMock).not.toHaveBeenCalled();
});
it("guards against unexpected async sync-resolution results" , () => {
resolveBoundaryPathSyncMock.mockReturnValue(
Promise.resolve({
canonicalPath: "/real/plugin.json" ,
rootCanonicalPath: "/real/root" ,
}),
);
const opened = openBoundaryFileSync({
absolutePath: "plugin.json" ,
rootPath: "/workspace" ,
boundaryLabel: "plugin root" ,
});
expect(opened.ok).toBe(false );
if (opened.ok) {
return ;
}
expect(opened.reason).toBe("validation" );
expect(String(opened.error)).toContain("Unexpected async boundary resolution" );
});
it("awaits async boundary resolution before verifying the file" , async () => {
const ioFs = { marker: "io" } as never;
const absolutePath = path.resolve("notes.txt" );
resolveBoundaryPathMock.mockResolvedValue({
canonicalPath: "/real/notes.txt" ,
rootCanonicalPath: "/real/root" ,
});
openVerifiedFileSyncMock.mockReturnValue({
ok: false ,
reason: "validation" ,
error: new Error("blocked" ),
});
const opened = await openBoundaryFile({
absolutePath: "notes.txt" ,
rootPath: "/workspace" ,
boundaryLabel: "workspace" ,
aliasPolicy: { allowFinalSymlinkForUnlink: true },
ioFs,
});
expect(resolveBoundaryPathMock).toHaveBeenCalledWith({
absolutePath,
rootPath: "/workspace" ,
rootCanonicalPath: undefined,
boundaryLabel: "workspace" ,
policy: { allowFinalSymlinkForUnlink: true },
skipLexicalRootCheck: undefined,
});
expect(openVerifiedFileSyncMock).toHaveBeenCalledWith({
filePath: absolutePath,
resolvedPath: "/real/notes.txt" ,
rejectHardlinks: true ,
maxBytes: undefined,
allowedType: undefined,
ioFs,
});
expect(opened).toEqual({
ok: false ,
reason: "validation" ,
error: expect.any(Error),
});
});
it("maps async boundary resolution failures to validation errors" , async () => {
const error = new Error("escaped" );
resolveBoundaryPathMock.mockRejectedValue(error);
const opened = await openBoundaryFile({
absolutePath: "notes.txt" ,
rootPath: "/workspace" ,
boundaryLabel: "workspace" ,
});
expect(opened).toEqual({
ok: false ,
reason: "validation" ,
error,
});
expect(openVerifiedFileSyncMock).not.toHaveBeenCalled();
});
it("matches boundary file failures by reason with fallback support" , () => {
const missing = matchBoundaryFileOpenFailure(
{ ok: false , reason: "path" , error: new Error("missing" ) },
{
path: () => "missing" ,
fallback: () => "fallback" ,
},
);
const io = matchBoundaryFileOpenFailure(
{ ok: false , reason: "io" , error: new Error("io" ) },
{
io: () => "io" ,
fallback: () => "fallback" ,
},
);
const validation = matchBoundaryFileOpenFailure(
{ ok: false , reason: "validation" , error: new Error("blocked" ) },
{
fallback: (failure) => failure.reason,
},
);
expect(missing).toBe("missing" );
expect(io).toBe("io" );
expect(validation).toBe("validation" );
});
});
Messung V0.5 in Prozent C=98 H=99 G=98
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-06-10)
¤
*© Formatika GbR, Deutschland