Files
rea/tests/boundary/cli/referenceSourceRuntimeAuthority.test.ts

222 lines
7.0 KiB
TypeScript
Raw Permalink Normal View History

import { createHash } from "node:crypto";
import { mkdir, writeFile } from "node:fs/promises";
import { dirname, join } from "node:path";
import { expect } from "vitest";
import { historicalSourceGraphSchema } from "../../../src/domain/referenceSourceGraph.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { cliTest, type TestCli } from "../../support/cli/cliFixture.js";
const writeSources = async (root: string, sources: Record<string, string>) => {
await Promise.all(
Object.entries(sources).map(async ([path, source]) => {
await mkdir(dirname(join(root, path)), { recursive: true });
await writeFile(join(root, path), source);
}),
);
};
const importGraph = async (cli: TestCli, root: string) => {
const config = await createTestTempDirectory(
"rea-reference-authority-config-",
);
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);
};
cliTest(
"shares runtime authority without promoting unresolved source contexts",
async ({ cli }) => {
const root = await createTestTempDirectory(
"rea-reference-runtime-authority-",
);
const request = 'require("./dep.cjs");\n';
const derived = [
'const { createRequire } = require("node:module");',
'const load = createRequire(import.meta.url); load("./dep.cjs");',
].join("\n");
const sources = {
"package.json": '{"type":"module"}\n',
".gitignore": "excluded/package.json\n",
"main.js": request,
"main.ts": request,
"derived.js": derived,
"derived.mjs": derived,
"main.cjs": request,
"main.cts": request,
"dep.cjs": "module.exports = 1;\n",
"untyped/package.json": "{}\n",
"untyped/main.js": request,
"untyped/dep.cjs": "module.exports = 2;\n",
"untyped/syntax.js": request + "export {};\n",
"untyped/erased.ts": 'import type { Stats } from "node:fs";\n' + request,
"unknown/package.json": "{\n",
"unknown/main.ts": request,
"unknown/main.jsx": request,
"unknown/main.tsx": request,
"unknown/factory.js": derived,
"unknown/dep.cjs": "module.exports = 3;\n",
"excluded/package.json": '{"type":"commonjs"}\n',
"excluded/main.js": request,
"excluded/dep.cjs": "module.exports = 4;\n",
"factory.mjs": [
'import { createRequire } from "node:module";',
'const load = createRequire(import.meta.url); load("./dep.cjs");',
].join("\n"),
};
await writeSources(root, sources);
const graph = await importGraph(cli, root);
expect(graph.parse_failures).toEqual([]);
const requires = graph.relationships.filter(
({ kind }) => kind === "requires",
);
for (const [from_path, to] of [
["main.cjs", "dep.cjs"],
["main.cts", "dep.cjs"],
["untyped/main.js", "untyped/dep.cjs"],
["factory.mjs", "dep.cjs"],
])
expect(requires).toContainEqual({
from_path,
to,
kind: "requires",
resolution: "internal",
parse_state: "parsed",
});
for (const from_path of [
"untyped/erased.ts",
"unknown/main.ts",
"unknown/main.jsx",
"unknown/main.tsx",
"unknown/factory.js",
"excluded/main.js",
]) {
expect(requires).toContainEqual({
from_path,
to: "./dep.cjs",
kind: "requires",
resolution: "unknown",
parse_state: "partial",
});
expect(graph.entries).toContainEqual(
expect.objectContaining({
path: from_path,
limitations: [expect.stringContaining("Module context is unknown")],
}),
);
}
expect(requires).toContainEqual({
from_path: "unknown/factory.js",
to: "node:module",
kind: "requires",
resolution: "unknown",
parse_state: "partial",
});
expect(requires).toHaveLength(11);
for (const path of [
"main.js",
"main.ts",
"derived.js",
"derived.mjs",
"untyped/syntax.js",
])
expect(requires.filter(({ from_path }) => from_path === path)).toEqual(
[],
);
expect(graph.entries).toContainEqual(
expect.objectContaining({
path: "derived.js",
sha256: createHash("sha256").update(derived).digest("hex"),
content_state: "hashed",
}),
);
},
);
cliTest(
"preserves owned CommonJS factories through erased TypeScript syntax",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-reference-ts-factory-");
const source = [
'import type { Stats } from "node:fs";',
'const { createRequire } = require("node:module");',
'const load = createRequire(__filename); load("./dep.cjs");',
].join("\n");
await writeSources(root, {
"package.json": '{"type":"module"}\n',
"main.cts": source,
"dep.cjs": "module.exports = 1;\n",
"cjs/package.json": '{"type":"commonjs"}\n',
"cjs/main.ts": source,
"cjs/dep.cjs": "module.exports = 2;\n",
});
const graph = await importGraph(cli, root);
expect(graph.parse_failures).toEqual([]);
expect(graph.relationships).toHaveLength(6);
for (const from_path of ["main.cts", "cjs/main.ts"]) {
expect(graph.relationships).toContainEqual({
from_path,
to: "node:fs",
kind: "imports",
resolution: "external",
parse_state: "parsed",
});
expect(graph.relationships).toContainEqual({
from_path,
to: "node:module",
kind: "requires",
resolution: "external",
parse_state: "parsed",
});
// __filename remains outside the supported createRequire base forms.
expect(graph.relationships).toContainEqual({
from_path,
to: "./dep.cjs",
kind: "requires",
resolution: "unknown",
parse_state: "partial",
});
}
},
);
cliTest(
"retains unknown bases when the selected root omits its package scope",
async ({ cli }) => {
const parent = await createTestTempDirectory(
"rea-reference-unseen-authority-",
);
await writeSources(parent, {
"package.json": '{"type":"module"}\n',
"selected/main.js": 'require("./dep.cjs");\n',
"selected/dep.cjs": "module.exports = 1;\n",
});
const graph = await importGraph(cli, join(parent, "selected"));
expect(graph.parse_failures).toEqual([]);
expect(graph.relationships).toEqual([
{
from_path: "main.js",
to: "./dep.cjs",
kind: "requires",
resolution: "unknown",
parse_state: "partial",
},
]);
expect(graph.entries.map(({ path }) => path)).toEqual([
"dep.cjs",
"main.js",
]);
expect(graph.limitations).toContainEqual(
expect.stringContaining("Module context is unknown"),
);
},
);