Files
rea/tests/boundary/cli/referenceSourceUtf8.test.ts
T
morluto ec271121e4 fix(artifacts): preserve path identity and exclusion applicability
Normalize path facts once at the owning filesystem boundary, preserve caller spelling and unavailable inventory members, and retain typed read failures. Record exclusion entry kinds so only proven directories can establish descendant symlink targets; omitted legacy kinds stay unknown.

Reviewed fixes include #1969, #2642, #2759, and #2994.

Validation: 26 reference graph source tests passed; prior artifact checks passed 275 tests with one Windows-only skip. Updated compiled import workflows are included in final validation after regeneration.
2026-10-14 08:31:16 +08:00

222 lines
7.1 KiB
TypeScript

import { createHash } from "node:crypto";
import {
mkdir,
readFile,
readlink,
symlink,
writeFile,
} from "node:fs/promises";
import { join } from "node:path";
import { expect } from "vitest";
import { historicalSourceGraphSchema } from "../../../src/domain/referenceSourceGraph.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { cliTest } from "../../support/cli/cliFixture.js";
cliTest(
"retains malformed source bytes without inventing decoded import targets",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-reference-utf8-");
const sources = new Map([
[
"invalid.mjs",
Buffer.concat([
Buffer.from('import "./caf'),
Buffer.from([0xff]),
Buffer.from('.js";\n'),
]),
],
[
"comment.ts",
Buffer.concat([
Buffer.from('import "./dep.js"; // '),
Buffer.from([0xc0, 0xaf]),
]),
],
["valid.mjs", Buffer.from('import "./caf�.js";\n')],
["caf�.js", Buffer.from("export const value = 1;\n")],
["dep.js", Buffer.from("export const value = 2;\n")],
]);
await Promise.all(
[...sources].map(([path, bytes]) => writeFile(join(root, path), bytes)),
);
const result = await cli.run({
arguments: ["import-reference-source", root, "--json"],
environment: { HOME: root, XDG_CONFIG_HOME: root, XDG_CACHE_HOME: root },
});
expect(result.exitCode, result.stderr + JSON.stringify(result.json)).toBe(
0,
);
const graph = historicalSourceGraphSchema.parse(result.json);
expect(graph.parse_failures).toEqual([
{
path: "comment.ts",
parser: "utf-8",
reason:
"Source bytes are not valid UTF-8; import targets were not parsed.",
},
{
path: "invalid.mjs",
parser: "utf-8",
reason:
"Source bytes are not valid UTF-8; import targets were not parsed.",
},
]);
expect(graph.relationships).toEqual([
{
from_path: "valid.mjs",
to: "caf�.js",
kind: "imports",
resolution: "internal",
parse_state: "parsed",
},
]);
for (const [path, bytes] of sources)
expect(graph.entries).toContainEqual(
expect.objectContaining({
path,
sha256: createHash("sha256").update(bytes).digest("hex"),
size: bytes.length,
content_state: "hashed",
}),
);
},
);
cliTest.skipIf(process.platform === "win32")(
"preserves historical literal-backslash entries and their exact owning import base",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-reference-literal-paths-");
const config = await createTestTempDirectory(
"rea-reference-literal-config-",
);
await mkdir(join(root, "literal"));
await mkdir(join(root, "literal\\dir"));
const sources = new Map([
["literal\\name.js", "export const value = 'literal';\n"],
["literal/name.js", "export const value = 'slash';\n"],
["literal\\dir/main.js", 'import "./dep.js";\n'],
["literal\\dir/dep.js", "export const value = 'owned';\n"],
]);
for (const [path, bytes] of sources)
await writeFile(join(root, path), bytes);
const result = await cli.run({
arguments: ["import-reference-source", root, "--json"],
environment: {
HOME: config,
XDG_CONFIG_HOME: config,
XDG_CACHE_HOME: config,
},
});
expect(result.exitCode, result.stderr + JSON.stringify(result.json)).toBe(
0,
);
const graph = historicalSourceGraphSchema.parse(result.json);
for (const [path, bytes] of sources)
expect(graph.entries).toContainEqual(
expect.objectContaining({
path,
sha256: createHash("sha256").update(bytes).digest("hex"),
content_state: "hashed",
}),
);
for (const [path, bytes] of sources)
expect(await readFile(join(root, path), "utf8")).toBe(bytes);
expect(graph.relationships).toContainEqual({
from_path: "literal\\dir/main.js",
to: "literal\\dir/dep.js",
kind: "imports",
resolution: "internal",
parse_state: "parsed",
});
},
);
cliTest.skipIf(process.platform === "win32")(
"preserves an observed historical symlink target with a literal backslash",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-reference-literal-link-");
const config = await createTestTempDirectory(
"rea-reference-literal-link-config-",
);
await writeFile(
join(root, "literal\\name.js"),
"export const value = 42;\n",
);
await symlink("literal\\name.js", join(root, "link.js"));
const result = await cli.run({
arguments: ["import-reference-source", root, "--json"],
environment: {
HOME: config,
XDG_CONFIG_HOME: config,
XDG_CACHE_HOME: config,
},
});
expect(result.exitCode, result.stderr + JSON.stringify(result.json)).toBe(
0,
);
const graph = historicalSourceGraphSchema.parse(result.json);
expect(await readlink(join(root, "link.js"))).toBe("literal\\name.js");
expect(await readFile(join(root, "literal\\name.js"), "utf8")).toBe(
"export const value = 42;\n",
);
expect(graph.entries).toContainEqual(
expect.objectContaining({
kind: "symlink",
path: "link.js",
target: "literal\\name.js",
target_state: "internal",
}),
);
},
);
cliTest.skipIf(process.platform === "win32")(
"imports an inventory-owned POSIX UNC-looking symlink target",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-reference-unc-literal-");
const config = await createTestTempDirectory("rea-reference-unc-config-");
const literal = "\\\\server\\share.js";
const literalBytes = "export const value = 'literal';\n";
const slashBytes = "export const value = 'slash';\n";
await mkdir(join(root, "server"));
await writeFile(join(root, literal), literalBytes);
await writeFile(join(root, "server", "share.js"), slashBytes);
await symlink(literal, join(root, "link.js"));
const result = await cli.run({
arguments: ["import-reference-source", root, "--json"],
environment: {
HOME: config,
XDG_CONFIG_HOME: config,
XDG_CACHE_HOME: config,
},
});
expect(result.exitCode, result.stderr + JSON.stringify(result.json)).toBe(
0,
);
const graph = historicalSourceGraphSchema.parse(result.json);
expect(graph.entries).toContainEqual(
expect.objectContaining({
kind: "symlink",
path: "link.js",
target: literal,
target_state: "internal",
}),
);
for (const [path, bytes] of [
[literal, literalBytes],
["server/share.js", slashBytes],
]) {
expect(graph.entries).toContainEqual(
expect.objectContaining({
path,
sha256: createHash("sha256").update(bytes!).digest("hex"),
}),
);
expect(await readFile(join(root, path!), "utf8")).toBe(bytes);
}
expect(await readlink(join(root, "link.js"))).toBe(literal);
},
);