Files
reverse-engineering-anything/tests/boundary/cli/referenceSourceSymlinkPaths.test.ts
T

135 lines
4.4 KiB
TypeScript

import {
mkdir,
readFile,
readlink,
symlink,
unlink,
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.skipIf(process.platform === "win32")(
"resolves source symlink targets through native traversal across retargeting and missing files",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-reference-native-links-");
const config = await createTestTempDirectory("rea-reference-link-config-");
const outside = await createTestTempDirectory(
"rea-reference-link-outside-",
);
for (const directory of [
join(root, "first"),
join(root, "second"),
outside,
]) {
await mkdir(join(directory, "deep"), { recursive: true });
await writeFile(
join(directory, "source.js"),
"export const selected = true;\n",
);
}
await writeFile(join(root, "source.js"), "export const decoy = true;\n");
const alias = join(root, "alias");
await symlink(join(root, "first/deep"), alias);
await symlink("alias/../source.js", join(root, "selected.js"));
await symlink("alias/../../source.js", join(root, "parent.js"));
await symlink("absent/../source.js", join(root, "missing-parent.js"));
await symlink("source.js/", join(root, "directory-required.js"));
await symlink("source.js/..", join(root, "file-parent.js"));
await symlink("source.js/../source.js", join(root, "file-parent-child.js"));
const run = async () => {
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,
);
return historicalSourceGraphSchema.parse(result.json);
};
const first = await run();
expect(first.entries).toEqual(
expect.arrayContaining([
expect.objectContaining({
path: "selected.js",
target: "first/source.js",
target_state: "internal",
}),
expect.objectContaining({
path: "parent.js",
target: "source.js",
target_state: "internal",
}),
expect.objectContaining({
path: "missing-parent.js",
target: `${root}/absent/../source.js`,
target_state: "missing",
}),
expect.objectContaining({
path: "directory-required.js",
target: `${root}/source.js/`,
target_state: "missing",
}),
expect.objectContaining({
path: "file-parent.js",
target: `${root}/source.js/..`,
target_state: "missing",
}),
expect.objectContaining({
path: "file-parent-child.js",
target: `${root}/source.js/../source.js`,
target_state: "missing",
}),
]),
);
await unlink(alias);
await symlink(join(root, "second/deep"), alias);
const second = await run();
expect(second.entries).toContainEqual(
expect.objectContaining({
path: "selected.js",
target: "second/source.js",
target_state: "internal",
}),
);
await unlink(join(root, "second/source.js"));
const missing = await run();
expect(missing.entries).toContainEqual(
expect.objectContaining({
path: "selected.js",
target: "second/source.js",
target_state: "missing",
}),
);
await unlink(alias);
await symlink(join(outside, "deep"), alias);
const external = await run();
expect(external.entries).toContainEqual(
expect.objectContaining({
path: "selected.js",
target: join(outside, "source.js"),
target_state: "external",
}),
);
expect(
external.entries
.filter((entry) => entry.kind === "file")
.map((entry) => entry.path),
).toEqual(["first/source.js", "source.js"]);
expect(await readlink(join(root, "selected.js"))).toBe(
"alias/../source.js",
);
expect(await readFile(join(root, "source.js"), "utf8")).toBe(
"export const decoy = true;\n",
);
},
);