// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
// Tests for dep-review.js — run with: node --test .github/scripts/dep-review.test.js
"use strict";
const { describe, it } = require("node:test");
const assert = require("node:assert/strict");
const {
parseExternalDeps,
fmtSource,
diffDeps,
buildSummary,
parseCratePathMap,
parseInternalDepGraph,
checkContainment,
diffContainmentViolations,
buildPolicySummary,
} = require("./dep-review.js");
// -- fixtures --
const LOCK_MINIMAL = `
# This file is automatically @generated by Cargo.
version = 4
[[package]]
name = "my_crate"
version = "0.0.0"
dependencies = [
"serde",
"helper",
]
[[package]]
name = "helper"
version = "0.0.0"
[[package]]
name = "serde"
version = "1.0.200"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abc"
[[package]]
name = "serde_derive"
version = "1.0.200"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "def"
`;
const MANIFEST_MINIMAL = `
[workspace]
members = ["my_crate", "helper", "lib_a", "lib_b"]
[workspace.dependencies]
my_crate = { path = "app/my_crate" }
helper = { path = "support/helper" }
lib_a = { path = "support/lib_a" }
lib_b = { path = "vm/lib_b" }
serde = "1.0"
`;
const POLICY = {
containment: [
{
prefix: "support/",
description: "support/ crates must not depend on crates outside support/",
},
],
};
// -- parseExternalDeps --
describe("parseExternalDeps", () => {
it("extracts external packages (those with source)", () => {
const deps = parseExternalDeps(LOCK_MINIMAL);
assert.equal(deps.size, 2);
assert.ok(
deps.has("serde\t1.0.200\tregistry+https://github.com/rust-lang/crates.io-index")
);
assert.ok(
deps.has("serde_derive\t1.0.200\tregistry+https://github.com/rust-lang/crates.io-index")
);
});
it("ignores internal packages (no source)", () => {
const deps = parseExternalDeps(LOCK_MINIMAL);
for (const key of deps) {
assert.ok(!key.startsWith("my_crate\t"));
assert.ok(!key.startsWith("helper\t"));
}
});
it("handles empty lockfile", () => {
const deps = parseExternalDeps("version = 4\n");
assert.equal(deps.size, 0);
});
it("handles git sources", () => {
const lock = `
[[package]]
name = "foo"
version = "0.1.0"
source = "git+https://github.com/org/foo?branch=main#abc123"
`;
const deps = parseExternalDeps(lock);
assert.equal(deps.size, 1);
const key = [...deps][0];
assert.ok(key.startsWith("foo\t0.1.0\tgit+"));
});
it("tracks multiple versions of the same crate", () => {
const lock = `
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aaa"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbb"
`;
const deps = parseExternalDeps(lock);
assert.equal(deps.size, 2);
assert.ok(
deps.has("windows-sys\t0.48.0\tregistry+https://github.com/rust-lang/crates.io-index")
);
assert.ok(
deps.has("windows-sys\t0.52.0\tregistry+https://github.com/rust-lang/crates.io-index")
);
});
});
// -- fmtSource --
describe("fmtSource", () => {
it("returns empty for registry source", () => {
assert.equal(fmtSource("registry+https://github.com/rust-lang/crates.io-index"), "");
});
it("formats git source with URL", () => {
const result = fmtSource("git+https://github.com/org/repo?branch=main#abc123");
assert.equal(result, " (https://github.com/org/repo?branch=main)");
});
it("passes through unknown sources", () => {
assert.equal(fmtSource("something-else"), " (something-else)");
});
});
// -- diffDeps --
describe("diffDeps", () => {
it("detects added crates", () => {
const base = new Set();
const pr = new Set(["foo\t1.0.0\tsrc"]);
const { added } = diffDeps(base, pr);
assert.equal(added.length, 1);
assert.equal(added[0].name, "foo");
});
it("detects version changes as additions", () => {
const base = new Set(["foo\t1.0.0\tsrc"]);
const pr = new Set(["foo\t2.0.0\tsrc"]);
const { added } = diffDeps(base, pr);
assert.equal(added.length, 1);
assert.equal(added[0].name, "foo");
assert.equal(added[0].version, "2.0.0");
});
it("does not report removals", () => {
const base = new Set(["foo\t1.0.0\tsrc"]);
const pr = new Set();
const { added } = diffDeps(base, pr);
assert.equal(added.length, 0);
});
it("reports nothing when identical", () => {
const base = new Set(["foo\t1.0.0\tsrc"]);
const pr = new Set(["foo\t1.0.0\tsrc"]);
const { added } = diffDeps(base, pr);
assert.equal(added.length, 0);
});
it("detects new version alongside existing version", () => {
const base = new Set(["foo\t1.0.0\tsrc"]);
const pr = new Set(["foo\t1.0.0\tsrc", "foo\t2.0.0\tsrc"]);
const { added } = diffDeps(base, pr);
assert.equal(added.length, 1);
assert.equal(added[0].version, "2.0.0");
});
});
// -- buildSummary --
describe("buildSummary", () => {
it("includes added crates", () => {
const summary = buildSummary({
added: [{ name: "foo", version: "1.0.0", source: "registry+x" }],
});
assert.ok(summary.includes("**New external crate versions:**"));
assert.ok(summary.includes("`foo` 1.0.0"));
});
});
// -- parseCratePathMap --
describe("parseCratePathMap", () => {
it("extracts path-based dependencies", () => {
const map = parseCratePathMap(MANIFEST_MINIMAL);
assert.equal(map.get("my_crate"), "app/my_crate");
assert.equal(map.get("helper"), "support/helper");
assert.equal(map.get("lib_a"), "support/lib_a");
assert.equal(map.get("lib_b"), "vm/lib_b");
});
it("ignores non-path dependencies", () => {
const map = parseCratePathMap(MANIFEST_MINIMAL);
assert.ok(!map.has("serde"));
});
it("handles complex inline tables", () => {
const toml = `foo = { path = "a/b", features = ["x"], default-features = false }`;
const map = parseCratePathMap(toml);
assert.equal(map.get("foo"), "a/b");
});
});
// -- parseInternalDepGraph --
describe("parseInternalDepGraph", () => {
it("extracts internal crates and their dependencies", () => {
const { graph, internalCrates } = parseInternalDepGraph(LOCK_MINIMAL);
assert.ok(internalCrates.has("my_crate"));
assert.ok(internalCrates.has("helper"));
assert.ok(!internalCrates.has("serde"));
assert.deepEqual(graph.get("my_crate"), ["serde", "helper"]);
assert.deepEqual(graph.get("helper"), []);
});
});
// -- checkContainment --
describe("checkContainment", () => {
it("flags violations when support/ crate depends on non-support/ crate", () => {
// helper (support/helper) depends on lib_b (vm/lib_b)
const lock = `
[[package]]
name = "helper"
version = "0.0.0"
dependencies = [
"lib_b",
]
[[package]]
name = "lib_b"
version = "0.0.0"
`;
const { graph, internalCrates } = parseInternalDepGraph(lock);
const pathMap = parseCratePathMap(MANIFEST_MINIMAL);
const violations = checkContainment(graph, internalCrates, pathMap, POLICY);
assert.equal(violations.length, 1);
assert.equal(violations[0].crate, "helper");
assert.equal(violations[0].dep, "lib_b");
});
it("allows support/ crate depending on other support/ crate", () => {
const lock = `
[[package]]
name = "helper"
version = "0.0.0"
dependencies = [
"lib_a",
]
[[package]]
name = "lib_a"
version = "0.0.0"
`;
const { graph, internalCrates } = parseInternalDepGraph(lock);
const pathMap = parseCratePathMap(MANIFEST_MINIMAL);
const violations = checkContainment(graph, internalCrates, pathMap, POLICY);
assert.equal(violations.length, 0);
});
it("ignores non-support/ crates depending on anything", () => {
const lock = `
[[package]]
name = "my_crate"
version = "0.0.0"
dependencies = [
"helper",
"lib_b",
]
[[package]]
name = "helper"
version = "0.0.0"
[[package]]
name = "lib_b"
version = "0.0.0"
`;
const { graph, internalCrates } = parseInternalDepGraph(lock);
const pathMap = parseCratePathMap(MANIFEST_MINIMAL);
const violations = checkContainment(graph, internalCrates, pathMap, POLICY);
assert.equal(violations.length, 0);
});
});
// -- diffContainmentViolations --
describe("diffContainmentViolations", () => {
const v1 = { crate: "a", dep: "b", cratePath: "support/a", depPath: "vm/b", rule: "test" };
const v2 = { crate: "c", dep: "d", cratePath: "support/c", depPath: "vm/d", rule: "test" };
it("returns only new violations", () => {
const result = diffContainmentViolations([v1], [v1, v2]);
assert.equal(result.length, 1);
assert.equal(result[0].crate, "c");
});
it("returns empty when no new violations", () => {
const result = diffContainmentViolations([v1, v2], [v1]);
assert.equal(result.length, 0);
});
it("returns empty when identical", () => {
const result = diffContainmentViolations([v1], [v1]);
assert.equal(result.length, 0);
});
});
// -- buildPolicySummary --
describe("buildPolicySummary", () => {
it("returns empty for no violations", () => {
assert.equal(buildPolicySummary([]), "");
});
it("formats violations", () => {
const summary = buildPolicySummary([
{ crate: "a", cratePath: "support/a", dep: "b", depPath: "vm/b", rule: "no cross" },
]);
assert.ok(summary.includes("`a` (support/a) → `b` (vm/b)"));
assert.ok(summary.includes("no cross"));
});
});
// -- integration: full lockfile round-trip --
describe("integration", () => {
it("no issues when base and PR are identical", () => {
const baseDeps = parseExternalDeps(LOCK_MINIMAL);
const prDeps = parseExternalDeps(LOCK_MINIMAL);
const diff = diffDeps(baseDeps, prDeps);
assert.equal(diff.added.length, 0);
const pathMap = parseCratePathMap(MANIFEST_MINIMAL);
const baseGraph = parseInternalDepGraph(LOCK_MINIMAL);
const prGraph = parseInternalDepGraph(LOCK_MINIMAL);
const baseV = checkContainment(baseGraph.graph, baseGraph.internalCrates, pathMap, POLICY);
const prV = checkContainment(prGraph.graph, prGraph.internalCrates, pathMap, POLICY);
const newV = diffContainmentViolations(baseV, prV);
assert.equal(newV.length, 0);
});
it("detects added external dep + new containment violation together", () => {
const prLock = LOCK_MINIMAL + `
[[package]]
name = "new_ext"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "xyz"
[[package]]
name = "lib_a"
version = "0.0.0"
dependencies = [
"lib_b",
]
[[package]]
name = "lib_b"
version = "0.0.0"
`;
// External diff
const baseDeps = parseExternalDeps(LOCK_MINIMAL);
const prDeps = parseExternalDeps(prLock);
const diff = diffDeps(baseDeps, prDeps);
assert.equal(diff.added.length, 1);
assert.equal(diff.added[0].name, "new_ext");
// Containment
const pathMap = parseCratePathMap(MANIFEST_MINIMAL);
const baseGraph = parseInternalDepGraph(LOCK_MINIMAL);
const prGraph = parseInternalDepGraph(prLock);
const baseV = checkContainment(baseGraph.graph, baseGraph.internalCrates, pathMap, POLICY);
const prV = checkContainment(prGraph.graph, prGraph.internalCrates, pathMap, POLICY);
const newV = diffContainmentViolations(baseV, prV);
assert.equal(newV.length, 1);
assert.equal(newV[0].crate, "lib_a");
assert.equal(newV[0].dep, "lib_b");
});
});microsoft/openvmm
Publicmirrored fromhttps://github.com/microsoft/openvmmAvailable
.github/scripts/dep-review.test.js
412lines · modepreview