Files
reverse-engineering-anything/tests/boundary/process/publishedPackageNpmSelection.test.ts
T

176 lines
6.8 KiB
TypeScript

import {
copyFile,
mkdir,
readFile,
symlink,
writeFile,
} from "node:fs/promises";
import { delimiter, join, resolve } from "node:path";
import { pathToFileURL } from "node:url";
import { expect } from "vitest";
import { z } from "zod";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { processTest } from "../../support/process/processFixture.js";
// These prerequisites isolate npm selection from registry availability, MCP
// conformance and updater behavior, which have their own integration suites.
const updaterProgram = `
import { writeFile } from "node:fs/promises";
import { join } from "node:path";
export async function runUpdate(previousVersion, host) {
await writeFile(join(host.root, "package.json"), JSON.stringify({ version: "6.9.0" }));
return { status: "updated", previousVersion, latestVersion: "6.9.0", installedVersion: "6.9.0", command: ["npm", "install", "rea-agents@6.9.0"] };
}
`;
const prerequisiteProgram = `
import { mkdir, writeFile } from "node:fs/promises";
import { join } from "node:path";
export const readNpmRelease = async () => ({ status: "published", taggedVersion: "6.9.0", stableVersion: "6.9.0" });
export const stableUpdateVersion = () => "6.9.0";
export const verifyInstalledCanaryPackage = async () => ({ controlled_protocol_prerequisite: true });
export async function installCanaryPackage(specifier, workspace) {
const root = join(workspace, "controlled-updater");
await mkdir(join(root, "dist/application"), { recursive: true });
await writeFile(join(root, "package.json"), '{"type":"module"}');
await writeFile(join(root, "dist/application/Update.js"), ${JSON.stringify(updaterProgram)});
await writeFile(join(root, "dist/application/UpdateRuntime.js"), 'export const systemUpdateHost = root => ({ root });');
return root;
}
`;
const npmProgram = (entry: string) => `#!${process.execPath}
import { appendFileSync, mkdirSync, writeFileSync } from "node:fs";
import { join } from "node:path";
const args = process.argv.slice(2);
appendFileSync(process.env.REA_RELEASE_NPM_LOG, JSON.stringify({ entry: ${JSON.stringify(entry)}, arguments: args, cwd: process.cwd(), marker: process.env.REA_RELEASE_NPM_MARKER }) + "\\n");
if (args[0] === "view") process.stdout.write(JSON.stringify(["6.8.0"]));
else if (args[0] === "install") {
const prefix = args[args.indexOf("--prefix") + 1];
const root = join(prefix, "lib/node_modules/rea-agents");
mkdirSync(root, { recursive: true });
writeFileSync(join(root, "package.json"), JSON.stringify({ version: "6.8.0" }));
mkdirSync(join(prefix, "bin"), { recursive: true });
writeFileSync(join(prefix, "bin/rea-agents"), ${JSON.stringify(`#!${process.execPath}
process.stdout.write(" update Update REA\\n");
`)}, { mode: 0o700 });
} else if (args[0] === "root") process.stdout.write(join(process.env.npm_config_prefix, "lib/node_modules"));
else throw new Error("Unexpected npm invocation: " + JSON.stringify(args));
`;
const prepare = async () => {
const root = await createTestTempDirectory("rea-published-npm-selection-");
const controller = join(root, "controller with spaces");
const bin = join(root, "bin");
await mkdir(join(controller, "scripts"), { recursive: true });
await mkdir(bin);
await writeFile(join(controller, "package.json"), '{"type":"module"}');
await copyFile(
"scripts/verify-published-package.mjs",
join(controller, "scripts/verify-published-package.mjs"),
);
await symlink(resolve("node_modules"), join(controller, "node_modules"));
const prerequisites = join(root, "prerequisites.mjs");
await writeFile(prerequisites, prerequisiteProgram);
const replacements = new Map([
["./lib/release-package-canary.mjs", pathToFileURL(prerequisites).href],
["./ci/npm-release.mjs", pathToFileURL(prerequisites).href],
...[
"verify-package-commands.mjs",
"verifier-cleanup.mjs",
"verifier-run.mjs",
].map(
(name) =>
[
`./lib/${name}`,
pathToFileURL(resolve("scripts/lib", name)).href,
] as const,
),
]);
const hook = join(root, "selected-dependencies.mjs");
await writeFile(
hook,
`import { registerHooks } from "node:module";
const selected = new Map(${JSON.stringify([...replacements])});
const owner = ${JSON.stringify(pathToFileURL(join(controller, "scripts/verify-published-package.mjs")).href)};
registerHooks({ resolve(specifier, context, next) {
return next(context.parentURL === owner ? selected.get(specifier) ?? specifier : specifier, context);
} });
`,
);
const selectedNpm = join(root, "selected npm entry.mjs");
await writeFile(selectedNpm, npmProgram("selected"));
await writeFile(join(bin, "npm"), npmProgram("PATH"), { mode: 0o700 });
return { root, controller, hook, selectedNpm, bin };
};
processTest.skipIf(process.platform === "win32").for([true, false])(
"preserves npm selection for all published-update operations (explicit selection: %s)",
async (selected, { processes }) => {
const { root, controller, hook, selectedNpm, bin } = await prepare();
const log = join(root, "npm-operations.jsonl");
const env = {
...process.env,
PATH: `${bin}${delimiter}${process.env.PATH ?? ""}`,
npm_execpath: selected ? selectedNpm : undefined,
REA_RELEASE_NPM_LOG: log,
REA_RELEASE_NPM_MARKER: "caller selection with spaces",
};
const result = await processes.run(
process.execPath,
["--import", hook, "scripts/verify-published-package.mjs", "6.9.0"],
{ cwd: controller, env, timeoutMs: 20_000 },
);
expect(result.exitCode, result.stderr).toBe(0);
const calls = z
.array(
z.object({
entry: z.string(),
arguments: z.array(z.string()),
cwd: z.string(),
marker: z.string(),
}),
)
.parse(
(await readFile(log, "utf8"))
.trim()
.split("\n")
.map((line) => JSON.parse(line)),
);
expect(calls).toHaveLength(3);
expect(calls.map(({ entry }) => entry)).toEqual(
Array(3).fill(selected ? "selected" : "PATH"),
);
expect(calls.map(({ marker }) => marker)).toEqual(
Array(3).fill("caller selection with spaces"),
);
const workspace = calls[0]!.cwd;
expect(calls.map(({ cwd }) => cwd)).toEqual([
workspace,
workspace,
workspace,
]);
expect(calls.map(({ arguments: args }) => args)).toEqual([
["view", "rea-agents", "versions", "--json"],
[
"install",
"--global",
"--ignore-scripts",
"--prefix",
join(workspace, "update-prefix"),
"rea-agents@6.8.0",
],
["root", "--global"],
]);
expect(JSON.parse(result.stdout)).toMatchObject({
version: "6.9.0",
update: {
status: "updated",
previousVersion: "6.8.0",
installedVersion: "6.9.0",
},
});
},
);