import { execFile } from "node:child_process"; import { chmod, mkdir, writeFile } from "node:fs/promises"; import { join } from "node:path"; import { promisify } from "node:util"; import { describe, expect, it } from "vitest"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; import { importReferenceSource, readReferenceSource, } from "../../support/referenceSourceResourceScope.js"; const importTree = (root: string) => importReferenceSource({ root, caller: "native-entry-failure-detail-test", policy: { secretPatterns: [] }, }); const permissionChecksUnavailable = process.platform === "win32" || process.getuid?.() === 0; describe("reference import native entry failure details", () => { it.skipIf(permissionChecksUnavailable)( "classifies a root behind a blocked parent as an I/O failure", async () => { const directory = await createTestTempDirectory( "rea-reference-root-permission-", ); const parent = join(directory, "blocked-parent"); const root = join(parent, "source"); await mkdir(root, { recursive: true }); await chmod(parent, 0); try { const read = await readReferenceSource(root); expect(read).toMatchObject({ ok: false, error: { tag: "reference-source-reader", code: "io", message: expect.stringContaining("EACCES"), }, }); } finally { await chmod(parent, 0o700); } }, ); it.skipIf(permissionChecksUnavailable)( "retains the actual operating-system reason for an unreadable file", async () => { const root = await createTestTempDirectory("rea-import-native-detail-"); const path = join(root, "blocked.ts"); await writeFile(path, "export const value = 1;\n"); await chmod(path, 0); try { const read = await readReferenceSource(root); if (!read.ok) throw new Error(read.error.message); const failure = read.value.entries.find( (entry) => entry.status === "failed" && entry.path === "blocked.ts", ); if (failure?.status !== "failed") throw new Error( "Owned permission fixture did not produce a read failure", ); expect(failure.message).toContain("EACCES"); const imported = await importTree(root); if (!imported.ok) throw new Error(imported.error.message); const entry = imported.value.entries.find( (item) => item.path === failure.path, ); expect(entry?.limitations.join(" ")).toContain(failure.message); } finally { await chmod(path, 0o600); } }, ); it.skipIf(process.platform === "win32")( "retains the unsupported file-type reason without suggesting another OS", async () => { const root = await createTestTempDirectory("rea-import-native-fifo-"); await promisify(execFile)("mkfifo", [join(root, "events.pipe")], { timeout: 2_000, }); const imported = await importTree(root); if (!imported.ok) throw new Error(imported.error.message); const entry = imported.value.entries.find( (item) => item.path === "events.pipe", ); expect(entry?.limitations.join(" ")).toContain( "Entry is not a regular file", ); expect(entry?.limitations.join(" ")).not.toContain("supported system"); }, ); it.skipIf(process.platform === "win32")( "excludes secret-patterned paths before failure projection", async () => { const root = await createTestTempDirectory("rea-import-native-secret-"); await promisify(execFile)("mkfifo", [join(root, "secret-events.pipe")], { timeout: 2_000, }); const imported = await importReferenceSource({ root, caller: "native-entry-secret-detail-test", policy: { secretPatterns: ["*secret*"] }, }); if (!imported.ok) throw new Error(imported.error.message); expect( imported.value.entries.find( (item) => item.path === "secret-events.pipe", ), ).toBeUndefined(); expect(imported.value.exclusions).toContainEqual({ path: "secret-events.pipe", reason: "configured-secret", pattern: "*secret*", }); }, ); it("preserves an ordinary readable source-file control", async () => { const root = await createTestTempDirectory("rea-import-native-readable-"); await writeFile(join(root, "main.ts"), "export const value = 1;\n"); const imported = await importTree(root); if (!imported.ok) throw new Error(imported.error.message); expect( imported.value.entries.find(({ path }) => path === "main.ts"), ).toMatchObject({ kind: "file", content_state: "hashed", limitations: [] }); }); });