mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 19:00:45 +00:00
613 lines
21 KiB
TypeScript
613 lines
21 KiB
TypeScript
import { mkdirSync, mkdtempSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { dirname, join } from "node:path";
|
|
import { describe, expect, it } from "vitest";
|
|
import { listBundledPluginPackArtifacts } from "../scripts/lib/bundled-plugin-build-entries.mjs";
|
|
import {
|
|
buildPublishedInstallCommandArgs,
|
|
buildPublishedInstallScenarios,
|
|
collectInstalledContextEngineRuntimeErrors,
|
|
collectInstalledRootDependencyManifestErrors,
|
|
collectInstalledMirroredRootDependencyManifestErrors,
|
|
collectInstalledPackageErrors,
|
|
normalizeInstalledBinaryVersion,
|
|
resolveInstalledBinaryPath,
|
|
} from "../scripts/openclaw-npm-postpublish-verify.ts";
|
|
import { BUNDLED_RUNTIME_SIDECAR_PATHS } from "../src/plugins/runtime-sidecar-paths.ts";
|
|
|
|
const PUBLISHED_BUNDLED_RUNTIME_SIDECAR_PATHS = BUNDLED_RUNTIME_SIDECAR_PATHS.filter(
|
|
(relativePath) => listBundledPluginPackArtifacts().includes(relativePath),
|
|
);
|
|
const REQUIRED_INSTALLED_RUNTIME_SIDECAR_PATHS = [
|
|
...PUBLISHED_BUNDLED_RUNTIME_SIDECAR_PATHS,
|
|
] as const;
|
|
|
|
describe("buildPublishedInstallScenarios", () => {
|
|
it("uses a single fresh scenario for plain stable releases", () => {
|
|
expect(buildPublishedInstallScenarios("2026.3.23")).toEqual([
|
|
{
|
|
name: "fresh-exact",
|
|
installSpecs: ["openclaw@2026.3.23"],
|
|
expectedVersion: "2026.3.23",
|
|
},
|
|
]);
|
|
});
|
|
|
|
it("adds a stable-to-correction upgrade scenario for correction releases", () => {
|
|
expect(buildPublishedInstallScenarios("2026.3.23-2")).toEqual([
|
|
{
|
|
name: "fresh-exact",
|
|
installSpecs: ["openclaw@2026.3.23-2"],
|
|
expectedVersion: "2026.3.23-2",
|
|
},
|
|
{
|
|
name: "upgrade-from-base-stable",
|
|
installSpecs: ["openclaw@2026.3.23", "openclaw@2026.3.23-2"],
|
|
expectedVersion: "2026.3.23-2",
|
|
},
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe("buildPublishedInstallCommandArgs", () => {
|
|
it("runs lifecycle scripts for published install verification", () => {
|
|
const args = buildPublishedInstallCommandArgs("/tmp/openclaw-prefix", "openclaw@2026.4.10");
|
|
|
|
expect(args).toEqual([
|
|
"install",
|
|
"-g",
|
|
"--prefix",
|
|
"/tmp/openclaw-prefix",
|
|
"openclaw@2026.4.10",
|
|
"--no-fund",
|
|
"--no-audit",
|
|
]);
|
|
expect(args).not.toContain("--ignore-scripts");
|
|
});
|
|
});
|
|
|
|
describe("collectInstalledPackageErrors", () => {
|
|
it("flags version mismatches and missing runtime sidecars", () => {
|
|
const errors = collectInstalledPackageErrors({
|
|
expectedVersion: "2026.3.23-2",
|
|
installedVersion: "2026.3.23",
|
|
packageRoot: "/tmp/empty-openclaw",
|
|
});
|
|
|
|
expect(errors[0]).toBe(
|
|
"installed package version mismatch: expected 2026.3.23-2, found 2026.3.23.",
|
|
);
|
|
expect(errors).toEqual(
|
|
expect.arrayContaining(
|
|
REQUIRED_INSTALLED_RUNTIME_SIDECAR_PATHS.map(
|
|
(relativePath) =>
|
|
`installed package is missing required bundled runtime sidecar: ${relativePath}`,
|
|
),
|
|
),
|
|
);
|
|
expect(errors.length).toBeGreaterThanOrEqual(
|
|
1 + REQUIRED_INSTALLED_RUNTIME_SIDECAR_PATHS.length,
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("collectInstalledContextEngineRuntimeErrors", () => {
|
|
function makeInstalledPackageRoot(): string {
|
|
return mkdtempSync(join(tmpdir(), "openclaw-postpublish-context-engine-"));
|
|
}
|
|
|
|
it("rejects packaged bundles with unresolved legacy context engine runtime loaders", () => {
|
|
const packageRoot = makeInstalledPackageRoot();
|
|
|
|
try {
|
|
mkdirSync(join(packageRoot, "dist"), { recursive: true });
|
|
writeFileSync(
|
|
join(packageRoot, "dist", "runtime-plugins-BUG.js"),
|
|
'throw new Error("Failed to load legacy context engine runtime.");\n',
|
|
"utf8",
|
|
);
|
|
|
|
expect(collectInstalledContextEngineRuntimeErrors(packageRoot)).toEqual([
|
|
"installed package includes unresolved legacy context engine runtime loader; rebuild with a bundler-traceable LegacyContextEngine import.",
|
|
]);
|
|
} finally {
|
|
rmSync(packageRoot, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it("accepts packaged bundles that inline the legacy context engine registration", () => {
|
|
const packageRoot = makeInstalledPackageRoot();
|
|
|
|
try {
|
|
mkdirSync(join(packageRoot, "dist"), { recursive: true });
|
|
writeFileSync(
|
|
join(packageRoot, "dist", "runtime-plugins-OK.js"),
|
|
"registerContextEngineForOwner('legacy', async () => new LegacyContextEngine());\n",
|
|
"utf8",
|
|
);
|
|
|
|
expect(collectInstalledContextEngineRuntimeErrors(packageRoot)).toEqual([]);
|
|
} finally {
|
|
rmSync(packageRoot, { recursive: true, force: true });
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("normalizeInstalledBinaryVersion", () => {
|
|
it("accepts decorated CLI version output", () => {
|
|
expect(normalizeInstalledBinaryVersion("OpenClaw 2026.4.8 (9ece252)")).toBe("2026.4.8");
|
|
expect(normalizeInstalledBinaryVersion("OpenClaw 2026.4.8-beta.1 (9ece252)")).toBe(
|
|
"2026.4.8-beta.1",
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("resolveInstalledBinaryPath", () => {
|
|
it("uses the Unix global bin path on non-Windows platforms", () => {
|
|
expect(resolveInstalledBinaryPath("/tmp/openclaw-prefix", "darwin")).toBe(
|
|
"/tmp/openclaw-prefix/bin/openclaw",
|
|
);
|
|
});
|
|
|
|
it("uses the Windows npm shim path on win32", () => {
|
|
expect(resolveInstalledBinaryPath("C:/openclaw-prefix", "win32")).toBe(
|
|
"C:/openclaw-prefix/openclaw.cmd",
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("collectInstalledMirroredRootDependencyManifestErrors", () => {
|
|
function makeInstalledPackageRoot(): string {
|
|
return mkdtempSync(join(tmpdir(), "openclaw-postpublish-installed-"));
|
|
}
|
|
|
|
function writePackageFile(root: string, relativePath: string, value: unknown): void {
|
|
const fullPath = join(root, relativePath);
|
|
mkdirSync(dirname(fullPath), { recursive: true });
|
|
writeFileSync(fullPath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
}
|
|
|
|
function writeSlackWebApiProbePackage(params: {
|
|
root: string;
|
|
importerSource?: string;
|
|
importerPath?: string;
|
|
mirroredRootRuntimeDependencies?: string[];
|
|
rootDependencies?: Record<string, string>;
|
|
rootOptionalDependencies?: Record<string, string>;
|
|
}): void {
|
|
writePackageFile(params.root, "package.json", {
|
|
version: "2026.4.10",
|
|
dependencies: params.rootDependencies,
|
|
optionalDependencies: params.rootOptionalDependencies,
|
|
openclaw: params.mirroredRootRuntimeDependencies
|
|
? {
|
|
bundle: {
|
|
mirroredRootRuntimeDependencies: params.mirroredRootRuntimeDependencies,
|
|
},
|
|
}
|
|
: undefined,
|
|
});
|
|
writePackageFile(params.root, "dist/extensions/slack/package.json", {
|
|
dependencies: {
|
|
"@slack/web-api": "^7.15.0",
|
|
},
|
|
});
|
|
const importerPath = params.importerPath ?? "dist/probe-Cz2PiFtC.js";
|
|
mkdirSync(join(params.root, "dist"), { recursive: true });
|
|
writeFileSync(
|
|
join(params.root, importerPath),
|
|
params.importerSource ?? 'import("@slack/web-api");\n',
|
|
"utf8",
|
|
);
|
|
}
|
|
|
|
it("flags bundled plugin deps imported by root dist when root mirrors are missing", () => {
|
|
const packageRoot = makeInstalledPackageRoot();
|
|
|
|
try {
|
|
writeSlackWebApiProbePackage({ root: packageRoot });
|
|
|
|
expect(collectInstalledMirroredRootDependencyManifestErrors(packageRoot)).toEqual([
|
|
"installed package root is missing mirrored bundled runtime dependency '@slack/web-api' for dist importers: probe-Cz2PiFtC.js. Add it to package.json dependencies/optionalDependencies or keep imports under dist/extensions/slack/.",
|
|
]);
|
|
} finally {
|
|
rmSync(packageRoot, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it("allows bundled plugin deps imported from their own extension dist without root mirrors", () => {
|
|
const packageRoot = makeInstalledPackageRoot();
|
|
|
|
try {
|
|
writeSlackWebApiProbePackage({
|
|
root: packageRoot,
|
|
importerPath: "dist/extensions/slack/client-Cz2PiFtC.js",
|
|
});
|
|
|
|
expect(collectInstalledMirroredRootDependencyManifestErrors(packageRoot)).toEqual([]);
|
|
} finally {
|
|
rmSync(packageRoot, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it("allows bundled plugin deps imported from root chunks sourced from their extension", () => {
|
|
const packageRoot = makeInstalledPackageRoot();
|
|
|
|
try {
|
|
writeSlackWebApiProbePackage({
|
|
root: packageRoot,
|
|
importerSource: '//#region extensions/slack/client.ts\nimport("@slack/web-api");\n',
|
|
});
|
|
|
|
expect(collectInstalledMirroredRootDependencyManifestErrors(packageRoot)).toEqual([]);
|
|
} finally {
|
|
rmSync(packageRoot, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it("does not require root mirrors for extension-only Matrix crypto deps", () => {
|
|
const packageRoot = makeInstalledPackageRoot();
|
|
|
|
try {
|
|
writePackageFile(packageRoot, "package.json", {
|
|
version: "2026.4.10",
|
|
dependencies: {},
|
|
});
|
|
writePackageFile(packageRoot, "dist/extensions/matrix/package.json", {
|
|
dependencies: {
|
|
"@matrix-org/matrix-sdk-crypto-nodejs": "^0.4.0",
|
|
"@matrix-org/matrix-sdk-crypto-wasm": "18.1.0",
|
|
},
|
|
});
|
|
writeFileSync(
|
|
join(packageRoot, "dist/extensions/matrix/crypto-node.runtime.js"),
|
|
'require("@matrix-org/matrix-sdk-crypto-nodejs");\n',
|
|
"utf8",
|
|
);
|
|
|
|
expect(collectInstalledMirroredRootDependencyManifestErrors(packageRoot)).toEqual([]);
|
|
} finally {
|
|
rmSync(packageRoot, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it("accepts mirrored root dependencies declared in package optionalDependencies", () => {
|
|
const packageRoot = makeInstalledPackageRoot();
|
|
|
|
try {
|
|
writePackageFile(packageRoot, "package.json", {
|
|
version: "2026.4.10",
|
|
optionalDependencies: {
|
|
"@discordjs/opus": "^0.10.0",
|
|
},
|
|
openclaw: {
|
|
bundle: {
|
|
mirroredRootRuntimeDependencies: ["@discordjs/opus"],
|
|
},
|
|
},
|
|
});
|
|
writePackageFile(packageRoot, "dist/extensions/discord/package.json", {
|
|
optionalDependencies: {
|
|
"@discordjs/opus": "^0.10.0",
|
|
},
|
|
});
|
|
mkdirSync(join(packageRoot, "dist"), { recursive: true });
|
|
writeFileSync(
|
|
join(packageRoot, "dist", "probe-Cz2PiFtC.js"),
|
|
'require("@discordjs/opus");\n',
|
|
"utf8",
|
|
);
|
|
|
|
expect(collectInstalledMirroredRootDependencyManifestErrors(packageRoot)).toEqual([]);
|
|
} finally {
|
|
rmSync(packageRoot, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it("does not compare root mirror dependency versions", () => {
|
|
const packageRoot = makeInstalledPackageRoot();
|
|
|
|
try {
|
|
writeSlackWebApiProbePackage({
|
|
root: packageRoot,
|
|
mirroredRootRuntimeDependencies: ["@slack/web-api"],
|
|
rootDependencies: {
|
|
"@slack/web-api": "^7.16.0",
|
|
},
|
|
});
|
|
|
|
expect(collectInstalledMirroredRootDependencyManifestErrors(packageRoot)).toEqual([]);
|
|
} finally {
|
|
rmSync(packageRoot, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it("flags malformed bundled extension manifests instead of silently skipping them", () => {
|
|
const packageRoot = makeInstalledPackageRoot();
|
|
|
|
try {
|
|
writePackageFile(packageRoot, "package.json", {
|
|
version: "2026.4.10",
|
|
dependencies: {},
|
|
});
|
|
mkdirSync(join(packageRoot, "dist/extensions/slack"), { recursive: true });
|
|
writeFileSync(
|
|
join(packageRoot, "dist/extensions/slack/package.json"),
|
|
'{\n "openclaw": { invalid json\n',
|
|
"utf8",
|
|
);
|
|
|
|
expect(collectInstalledMirroredRootDependencyManifestErrors(packageRoot)).toEqual([
|
|
expect.stringContaining("installed bundled extension manifest invalid: failed to parse"),
|
|
]);
|
|
} finally {
|
|
rmSync(packageRoot, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it("flags bundled extension directories that are missing package.json", () => {
|
|
const packageRoot = makeInstalledPackageRoot();
|
|
|
|
try {
|
|
writePackageFile(packageRoot, "package.json", {
|
|
version: "2026.4.10",
|
|
dependencies: {},
|
|
});
|
|
mkdirSync(join(packageRoot, "dist/extensions/slack"), { recursive: true });
|
|
|
|
expect(collectInstalledMirroredRootDependencyManifestErrors(packageRoot)).toEqual([
|
|
`installed bundled extension manifest missing: ${join(packageRoot, "dist/extensions/slack/package.json")}.`,
|
|
]);
|
|
} finally {
|
|
rmSync(packageRoot, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it("skips manifest-only sidecar directories without package.json", () => {
|
|
const packageRoot = makeInstalledPackageRoot();
|
|
|
|
try {
|
|
writePackageFile(packageRoot, "package.json", {
|
|
version: "2026.4.10",
|
|
dependencies: {},
|
|
});
|
|
writePackageFile(packageRoot, "dist/extensions/device-pair/openclaw.plugin.json", {
|
|
id: "device-pair",
|
|
});
|
|
|
|
expect(collectInstalledMirroredRootDependencyManifestErrors(packageRoot)).toEqual([]);
|
|
} finally {
|
|
rmSync(packageRoot, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it("accepts legacy qa channel sidecar directories without package.json", () => {
|
|
const packageRoot = makeInstalledPackageRoot();
|
|
|
|
try {
|
|
writePackageFile(packageRoot, "package.json", {
|
|
version: "2026.4.10",
|
|
dependencies: {},
|
|
});
|
|
mkdirSync(join(packageRoot, "dist/extensions/qa-channel"), { recursive: true });
|
|
writeFileSync(
|
|
join(packageRoot, "dist/extensions/qa-channel/runtime-api.js"),
|
|
"export {};\n",
|
|
"utf8",
|
|
);
|
|
|
|
expect(collectInstalledMirroredRootDependencyManifestErrors(packageRoot)).toEqual([]);
|
|
} finally {
|
|
rmSync(packageRoot, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it("rejects bundled extension manifests that are not regular files", () => {
|
|
const packageRoot = makeInstalledPackageRoot();
|
|
const outsideManifestRoot = makeInstalledPackageRoot();
|
|
|
|
try {
|
|
writePackageFile(packageRoot, "package.json", {
|
|
version: "2026.4.10",
|
|
dependencies: {},
|
|
});
|
|
writePackageFile(outsideManifestRoot, "package.json", {
|
|
dependencies: {
|
|
"@slack/web-api": "^7.15.0",
|
|
},
|
|
});
|
|
mkdirSync(join(packageRoot, "dist/extensions/slack"), { recursive: true });
|
|
symlinkSync(
|
|
join(outsideManifestRoot, "package.json"),
|
|
join(packageRoot, "dist/extensions/slack/package.json"),
|
|
);
|
|
|
|
expect(collectInstalledMirroredRootDependencyManifestErrors(packageRoot)).toEqual([
|
|
expect.stringContaining("installed bundled extension manifest invalid: failed to parse"),
|
|
]);
|
|
expect(collectInstalledMirroredRootDependencyManifestErrors(packageRoot)[0]).toContain(
|
|
"manifest must be a regular file",
|
|
);
|
|
} finally {
|
|
rmSync(packageRoot, { recursive: true, force: true });
|
|
rmSync(outsideManifestRoot, { recursive: true, force: true });
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("collectInstalledRootDependencyManifestErrors", () => {
|
|
function makeInstalledPackageRoot(): string {
|
|
return mkdtempSync(join(tmpdir(), "openclaw-postpublish-root-deps-"));
|
|
}
|
|
|
|
function writePackageFile(root: string, relativePath: string, value: unknown): void {
|
|
const fullPath = join(root, relativePath);
|
|
mkdirSync(dirname(fullPath), { recursive: true });
|
|
writeFileSync(fullPath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
}
|
|
|
|
it("flags root dist imports whose declared runtime package name is missing", () => {
|
|
const packageRoot = makeInstalledPackageRoot();
|
|
|
|
try {
|
|
writePackageFile(packageRoot, "package.json", {
|
|
version: "2026.4.22",
|
|
dependencies: {},
|
|
});
|
|
mkdirSync(join(packageRoot, "dist"), { recursive: true });
|
|
writeFileSync(
|
|
join(packageRoot, "dist", "typebox-CXXonh2u.js"),
|
|
'import { Type } from "typebox";\nexport { Type };\n',
|
|
"utf8",
|
|
);
|
|
|
|
expect(collectInstalledRootDependencyManifestErrors(packageRoot)).toEqual([
|
|
"installed package root is missing declared runtime dependency 'typebox' for dist importers: typebox-CXXonh2u.js. Add it to package.json dependencies/optionalDependencies.",
|
|
]);
|
|
} finally {
|
|
rmSync(packageRoot, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it("accepts root dist imports when the runtime package name is declared", () => {
|
|
const packageRoot = makeInstalledPackageRoot();
|
|
|
|
try {
|
|
writePackageFile(packageRoot, "package.json", {
|
|
version: "2026.4.22",
|
|
dependencies: {
|
|
typebox: "1.1.28",
|
|
},
|
|
});
|
|
mkdirSync(join(packageRoot, "dist"), { recursive: true });
|
|
writeFileSync(
|
|
join(packageRoot, "dist", "typebox-CXXonh2u.js"),
|
|
'import { Type } from "typebox";\nexport { Type };\n',
|
|
"utf8",
|
|
);
|
|
|
|
expect(collectInstalledRootDependencyManifestErrors(packageRoot)).toEqual([]);
|
|
} finally {
|
|
rmSync(packageRoot, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it("flags undeclared imports from mjs and cjs root dist files", () => {
|
|
const packageRoot = makeInstalledPackageRoot();
|
|
|
|
try {
|
|
writePackageFile(packageRoot, "package.json", {
|
|
version: "2026.4.22",
|
|
dependencies: {},
|
|
});
|
|
mkdirSync(join(packageRoot, "dist"), { recursive: true });
|
|
writeFileSync(
|
|
join(packageRoot, "dist", "esm-entry.mjs"),
|
|
'export { value } from "mjs-only";\n',
|
|
"utf8",
|
|
);
|
|
writeFileSync(
|
|
join(packageRoot, "dist", "cjs-entry.cjs"),
|
|
'const cjsOnly = require("cjs-only");\nmodule.exports = cjsOnly;\n',
|
|
"utf8",
|
|
);
|
|
|
|
expect(collectInstalledRootDependencyManifestErrors(packageRoot)).toEqual([
|
|
"installed package root is missing declared runtime dependency 'cjs-only' for dist importers: cjs-entry.cjs. Add it to package.json dependencies/optionalDependencies.",
|
|
"installed package root is missing declared runtime dependency 'mjs-only' for dist importers: esm-entry.mjs. Add it to package.json dependencies/optionalDependencies.",
|
|
]);
|
|
} finally {
|
|
rmSync(packageRoot, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it("ignores import-like text inside comments", () => {
|
|
const packageRoot = makeInstalledPackageRoot();
|
|
|
|
try {
|
|
writePackageFile(packageRoot, "package.json", {
|
|
version: "2026.4.22",
|
|
dependencies: {},
|
|
});
|
|
mkdirSync(join(packageRoot, "dist"), { recursive: true });
|
|
writeFileSync(
|
|
join(packageRoot, "dist", "comment-only.js"),
|
|
[
|
|
'// import "fake-package";',
|
|
'/* require("fake-package-two"); */',
|
|
"export const ok = true;",
|
|
"",
|
|
].join("\n"),
|
|
"utf8",
|
|
);
|
|
|
|
expect(collectInstalledRootDependencyManifestErrors(packageRoot)).toEqual([]);
|
|
} finally {
|
|
rmSync(packageRoot, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it("ignores import-like text inside string literals", () => {
|
|
const packageRoot = makeInstalledPackageRoot();
|
|
|
|
try {
|
|
writePackageFile(packageRoot, "package.json", {
|
|
version: "2026.4.22",
|
|
dependencies: {},
|
|
});
|
|
mkdirSync(join(packageRoot, "dist"), { recursive: true });
|
|
writeFileSync(
|
|
join(packageRoot, "dist", "string-only.js"),
|
|
[
|
|
"export const help = \"run import('fake-package') after setup\";",
|
|
'export const note = "from \\"fake-package-two\\"";',
|
|
"",
|
|
].join("\n"),
|
|
"utf8",
|
|
);
|
|
|
|
expect(collectInstalledRootDependencyManifestErrors(packageRoot)).toEqual([]);
|
|
} finally {
|
|
rmSync(packageRoot, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it("returns a structured error when installed package.json is invalid", () => {
|
|
const packageRoot = makeInstalledPackageRoot();
|
|
|
|
try {
|
|
mkdirSync(join(packageRoot, "dist"), { recursive: true });
|
|
writeFileSync(join(packageRoot, "package.json"), "{not-json\n", "utf8");
|
|
|
|
expect(collectInstalledRootDependencyManifestErrors(packageRoot)).toEqual([
|
|
expect.stringMatching(/^installed package\.json could not be parsed:/u),
|
|
]);
|
|
} finally {
|
|
rmSync(packageRoot, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it("refuses oversized root dist files", () => {
|
|
const packageRoot = makeInstalledPackageRoot();
|
|
|
|
try {
|
|
writePackageFile(packageRoot, "package.json", {
|
|
version: "2026.4.22",
|
|
dependencies: {},
|
|
});
|
|
mkdirSync(join(packageRoot, "dist"), { recursive: true });
|
|
writeFileSync(
|
|
join(packageRoot, "dist", "oversized.js"),
|
|
"x".repeat(2 * 1024 * 1024 + 1),
|
|
"utf8",
|
|
);
|
|
|
|
expect(collectInstalledRootDependencyManifestErrors(packageRoot)).toEqual([
|
|
"installed package root dist file 'oversized.js' is invalid or exceeds 2097152 bytes.",
|
|
]);
|
|
} finally {
|
|
rmSync(packageRoot, { recursive: true, force: true });
|
|
}
|
|
});
|
|
});
|