143 lines
5.0 KiB
JavaScript
143 lines
5.0 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
|
|
import {
|
|
buildReleasePackagePlan,
|
|
checkConfiguration,
|
|
findUnpublishableWorkspaceEdges,
|
|
getReleasePackages,
|
|
} from "./release-package-map.mjs";
|
|
|
|
function pkg(name, { publishFromCi, ...deps } = {}) {
|
|
return { name, dir: name, publishFromCi, pkg: { name, ...deps } };
|
|
}
|
|
|
|
test("release package manifest covers all public packages with explicit CI enrollment", () => {
|
|
const packages = buildReleasePackagePlan();
|
|
assert.ok(packages.length > 0);
|
|
assert.ok(packages.every((pkg) => typeof pkg.publishFromCi === "boolean"));
|
|
});
|
|
|
|
test("release package list only contains CI-enrolled packages", () => {
|
|
const enabledPackages = getReleasePackages();
|
|
assert.ok(enabledPackages.length > 0);
|
|
assert.ok(enabledPackages.every((pkg) => pkg.publishFromCi === true));
|
|
});
|
|
|
|
test("release package list publishes the installable channel entrypoint last", () => {
|
|
const enabledPackages = getReleasePackages();
|
|
|
|
assert.equal(enabledPackages.at(-1)?.name, "paperclipai");
|
|
assert.ok(enabledPackages.slice(0, -1).some((pkg) => pkg.name === "@paperclipai/server"));
|
|
});
|
|
|
|
test("release package list keeps runtime workspace dependencies ahead of consumers", () => {
|
|
const enabledPackages = getReleasePackages();
|
|
const publishIndexByName = new Map(enabledPackages.map((pkg, index) => [pkg.name, index]));
|
|
|
|
for (const pkg of enabledPackages) {
|
|
for (const section of ["dependencies", "optionalDependencies", "peerDependencies"]) {
|
|
for (const [dependencyName, spec] of Object.entries(pkg.pkg[section] ?? {})) {
|
|
if (typeof spec !== "string" || !spec.startsWith("workspace:")) continue;
|
|
const dependencyIndex = publishIndexByName.get(dependencyName);
|
|
if (dependencyIndex === undefined) continue;
|
|
|
|
assert.ok(
|
|
dependencyIndex < publishIndexByName.get(pkg.name),
|
|
`${dependencyName} must publish before ${pkg.name}`,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
test("Hermes release surface publishes the unified built-in package and keeps gateway as a shim", () => {
|
|
const packages = buildReleasePackagePlan();
|
|
const hermes = packages.find((pkg) => pkg.name === "@paperclipai/hermes-paperclip-adapter");
|
|
const gatewayShim = packages.find((pkg) => pkg.name === "@paperclipai/adapter-hermes-gateway");
|
|
|
|
assert.equal(hermes?.dir, "packages/adapters/hermes");
|
|
assert.equal(hermes?.publishFromCi, true);
|
|
assert.equal(gatewayShim?.dir, "packages/adapters/hermes-gateway");
|
|
assert.equal(gatewayShim?.publishFromCi, false);
|
|
});
|
|
|
|
test("release package configuration validates successfully", () => {
|
|
assert.doesNotThrow(() => checkConfiguration());
|
|
});
|
|
|
|
test("guard flags a publishFromCi:true package depending on a publishFromCi:false package", () => {
|
|
const problems = findUnpublishableWorkspaceEdges([
|
|
pkg("@paperclipai/server", {
|
|
publishFromCi: true,
|
|
dependencies: { "@paperclipai/skills-catalog": "workspace:*" },
|
|
}),
|
|
pkg("@paperclipai/skills-catalog", { publishFromCi: false }),
|
|
]);
|
|
|
|
assert.equal(problems.length, 1);
|
|
assert.match(problems[0], /@paperclipai\/server/);
|
|
assert.match(problems[0], /@paperclipai\/skills-catalog/);
|
|
});
|
|
|
|
test("guard inspects optional and peer dependency sections too", () => {
|
|
const problems = findUnpublishableWorkspaceEdges([
|
|
pkg("@paperclipai/server", {
|
|
publishFromCi: true,
|
|
optionalDependencies: { "@paperclipai/opt": "workspace:^" },
|
|
peerDependencies: { "@paperclipai/peer": "workspace:*" },
|
|
}),
|
|
pkg("@paperclipai/opt", { publishFromCi: false }),
|
|
pkg("@paperclipai/peer", { publishFromCi: false }),
|
|
]);
|
|
|
|
assert.equal(problems.length, 2);
|
|
});
|
|
|
|
test("guard treats a workspace dep on an unknown @paperclipai package as unpublishable", () => {
|
|
const problems = findUnpublishableWorkspaceEdges([
|
|
pkg("@paperclipai/server", {
|
|
publishFromCi: true,
|
|
dependencies: { "@paperclipai/private-internal": "workspace:*" },
|
|
}),
|
|
]);
|
|
|
|
assert.equal(problems.length, 1);
|
|
});
|
|
|
|
test("guard allows true->true workspace edges", () => {
|
|
const problems = findUnpublishableWorkspaceEdges([
|
|
pkg("@paperclipai/server", {
|
|
publishFromCi: true,
|
|
dependencies: { "@paperclipai/shared": "workspace:*" },
|
|
}),
|
|
pkg("@paperclipai/shared", { publishFromCi: true }),
|
|
]);
|
|
|
|
assert.deepEqual(problems, []);
|
|
});
|
|
|
|
test("guard ignores non-workspace specs, non-internal deps, and edges from off-train packages", () => {
|
|
const problems = findUnpublishableWorkspaceEdges([
|
|
pkg("@paperclipai/server", {
|
|
publishFromCi: true,
|
|
dependencies: {
|
|
"@paperclipai/pinned": "0.3.1",
|
|
zod: "^3.0.0",
|
|
},
|
|
}),
|
|
pkg("@paperclipai/pinned", { publishFromCi: false }),
|
|
pkg("@paperclipai/offtrain", {
|
|
publishFromCi: false,
|
|
dependencies: { "@paperclipai/also-off": "workspace:*" },
|
|
}),
|
|
pkg("@paperclipai/also-off", { publishFromCi: false }),
|
|
]);
|
|
|
|
assert.deepEqual(problems, []);
|
|
});
|
|
|
|
test("the live release manifest has no unpublishable workspace edges", () => {
|
|
assert.deepEqual(findUnpublishableWorkspaceEdges(buildReleasePackagePlan()), []);
|
|
});
|