70bf21e064
Deploy (to testing) and Test Playground Preview Worker / Deploy Playground Preview Worker (testing) (push) Has been skipped
Deploy Workers Shared Staging / Deploy Workers Shared Staging (push) Failing after 0s
Prerelease / build (push) Has been skipped
Handle Changesets / Handle Changesets (push) Has been cancelled
Semgrep OSS scan / semgrep-oss (push) Has been cancelled
199 lines
6.1 KiB
TypeScript
199 lines
6.1 KiB
TypeScript
import { describe, it } from "vitest";
|
|
import { parseCatalog } from "../validate-catalog-usage";
|
|
import {
|
|
isPinnedVersion,
|
|
validateCatalogPins,
|
|
validatePackagePins,
|
|
} from "../validate-pinned-dependencies";
|
|
|
|
describe("isPinnedVersion()", () => {
|
|
it("should accept exact versions", ({ expect }) => {
|
|
expect(isPinnedVersion("1.2.3")).toBe(true);
|
|
expect(isPinnedVersion("0.0.14")).toBe(true);
|
|
expect(isPinnedVersion("1.20260529.1")).toBe(true);
|
|
});
|
|
|
|
it("should accept exact prerelease and build versions", ({ expect }) => {
|
|
expect(isPinnedVersion("4.1.0-beta.10")).toBe(true);
|
|
expect(isPinnedVersion("2.0.0-rc.24")).toBe(true);
|
|
expect(isPinnedVersion("1.2.3+build.5")).toBe(true);
|
|
});
|
|
|
|
it("should reject caret and tilde ranges", ({ expect }) => {
|
|
expect(isPinnedVersion("^1.2.3")).toBe(false);
|
|
expect(isPinnedVersion("~5.8.3")).toBe(false);
|
|
});
|
|
|
|
it("should reject comparator ranges", ({ expect }) => {
|
|
expect(isPinnedVersion(">1.20260305.0 <2.0.0-0")).toBe(false);
|
|
expect(isPinnedVersion(">=1.0.0")).toBe(false);
|
|
expect(isPinnedVersion("^6.1.0 || ^7.0.0 || ^8.0.0")).toBe(false);
|
|
});
|
|
|
|
it("should reject wildcards and partial versions", ({ expect }) => {
|
|
expect(isPinnedVersion("*")).toBe(false);
|
|
expect(isPinnedVersion("1.x")).toBe(false);
|
|
expect(isPinnedVersion("1.2")).toBe(false);
|
|
expect(isPinnedVersion("1")).toBe(false);
|
|
});
|
|
|
|
it("should reject non-version specifiers", ({ expect }) => {
|
|
expect(isPinnedVersion("")).toBe(false);
|
|
expect(isPinnedVersion("latest")).toBe(false);
|
|
expect(isPinnedVersion("workspace:*")).toBe(false);
|
|
expect(isPinnedVersion("catalog:default")).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("validateCatalogPins()", () => {
|
|
it("should pass when all entries are pinned", ({ expect }) => {
|
|
const errors = validateCatalogPins(
|
|
new Map([
|
|
["undici", "7.24.8"],
|
|
["esbuild", "0.28.1"],
|
|
["youch", "4.1.0-beta.10"],
|
|
])
|
|
);
|
|
expect(errors).toEqual([]);
|
|
});
|
|
|
|
it("should flag ranged entries", ({ expect }) => {
|
|
const errors = validateCatalogPins(
|
|
new Map([
|
|
["undici", "7.24.8"],
|
|
["ci-info", "^4.4.0"],
|
|
["typescript", "~5.8.3"],
|
|
])
|
|
);
|
|
expect(errors).toHaveLength(2);
|
|
expect(errors[0]).toContain('"ci-info"');
|
|
expect(errors[0]).toContain('"^4.4.0"');
|
|
expect(errors[1]).toContain('"typescript"');
|
|
});
|
|
|
|
it("should skip entries in the exceptions allowlist", ({ expect }) => {
|
|
const errors = validateCatalogPins(
|
|
new Map([["@cloudflare/workers-types", "^4.20260529.1"]]),
|
|
new Set(["@cloudflare/workers-types"])
|
|
);
|
|
expect(errors).toEqual([]);
|
|
});
|
|
|
|
it("should still flag non-excepted ranged entries", ({ expect }) => {
|
|
const errors = validateCatalogPins(
|
|
new Map([
|
|
["@cloudflare/workers-types", "^4.20260529.1"],
|
|
["vite", "^8.0.12"],
|
|
]),
|
|
new Set(["@cloudflare/workers-types"])
|
|
);
|
|
expect(errors).toHaveLength(1);
|
|
expect(errors[0]).toContain('"vite"');
|
|
});
|
|
|
|
it("should use the default exceptions allowlist", ({ expect }) => {
|
|
const errors = validateCatalogPins(
|
|
new Map([["@cloudflare/workers-types", "^4.20260529.1"]])
|
|
);
|
|
expect(errors).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe("validatePackagePins()", () => {
|
|
it("should pass when all dependencies are pinned", ({ expect }) => {
|
|
const errors = validatePackagePins("test-package", "test-package", {
|
|
name: "test-package",
|
|
dependencies: { "blake3-wasm": "2.1.5", "path-to-regexp": "6.3.0" },
|
|
});
|
|
expect(errors).toEqual([]);
|
|
});
|
|
|
|
it("should flag ranged dependencies", ({ expect }) => {
|
|
const errors = validatePackagePins("test-package", "test-package", {
|
|
name: "test-package",
|
|
dependencies: { sharp: "^0.34.5" },
|
|
});
|
|
expect(errors).toHaveLength(1);
|
|
expect(errors[0]).toContain('"sharp"');
|
|
expect(errors[0]).toContain('"^0.34.5"');
|
|
expect(errors[0]).toContain("dependencies");
|
|
});
|
|
|
|
it("should flag ranged optionalDependencies", ({ expect }) => {
|
|
const errors = validatePackagePins("test-package", "test-package", {
|
|
name: "test-package",
|
|
optionalDependencies: { fsevents: "~2.3.2" },
|
|
});
|
|
expect(errors).toHaveLength(1);
|
|
expect(errors[0]).toContain('"fsevents"');
|
|
expect(errors[0]).toContain("optionalDependencies");
|
|
});
|
|
|
|
it("should skip workspace, catalog, npm, link and file specifiers", ({
|
|
expect,
|
|
}) => {
|
|
const errors = validatePackagePins("test-package", "test-package", {
|
|
name: "test-package",
|
|
dependencies: {
|
|
miniflare: "workspace:*",
|
|
"@cloudflare/kv-asset-handler": "workspace:^",
|
|
esbuild: "catalog:default",
|
|
aliased: "npm:other@1.2.3",
|
|
linked: "link:../other",
|
|
filed: "file:../other",
|
|
},
|
|
});
|
|
expect(errors).toEqual([]);
|
|
});
|
|
|
|
it("should ignore peerDependencies and devDependencies", ({ expect }) => {
|
|
const errors = validatePackagePins("test-package", "test-package", {
|
|
name: "test-package",
|
|
peerDependencies: { vitest: "^4.1.0", react: "^17.0.2 || ^18.2.21" },
|
|
devDependencies: { typescript: "^5.0.0", esbuild: "^0.20.0" },
|
|
});
|
|
expect(errors).toEqual([]);
|
|
});
|
|
|
|
it("should report multiple violations across sections", ({ expect }) => {
|
|
const errors = validatePackagePins("test-package", "test-package", {
|
|
name: "test-package",
|
|
dependencies: { zod: "^3.25.76", "blake3-wasm": "2.1.5" },
|
|
optionalDependencies: { fsevents: "~2.3.2" },
|
|
});
|
|
expect(errors).toHaveLength(2);
|
|
expect(errors[0]).toContain('"zod"');
|
|
expect(errors[1]).toContain('"fsevents"');
|
|
});
|
|
});
|
|
|
|
describe("parseCatalog()", () => {
|
|
it("should parse quoted and unquoted entries", ({ expect }) => {
|
|
const catalog = parseCatalog(
|
|
[
|
|
"packages:",
|
|
" - packages/*",
|
|
"",
|
|
"minimumReleaseAge: 1440",
|
|
"",
|
|
"catalog:",
|
|
" # a comment",
|
|
' "@cloudflare/workers-types": "^4.20260529.1"',
|
|
' undici: "7.24.8"',
|
|
" '@vitest/runner': 4.1.0",
|
|
' typescript: "~5.8.3"',
|
|
"",
|
|
"overrides:",
|
|
' "@types/node": "$@types/node"',
|
|
].join("\n")
|
|
);
|
|
|
|
expect(catalog.get("@cloudflare/workers-types")).toBe("^4.20260529.1");
|
|
expect(catalog.get("undici")).toBe("7.24.8");
|
|
expect(catalog.get("@vitest/runner")).toBe("4.1.0");
|
|
expect(catalog.get("typescript")).toBe("~5.8.3");
|
|
// Should stop at the next top-level key.
|
|
expect(catalog.has("@types/node")).toBe(false);
|
|
});
|
|
});
|