Files
bun.sh/test/cli/install/catalogs.test.ts
Dylan Conway aad4d800ff add "configVersion" to bun.lock(b) (#24236)
### What does this PR do?

Adds `"configVersion"` to bun.lock(b). The version will be used to keep
default settings the same if they would be breaking across bun versions.

fixes ENG-21389
fixes ENG-21388
### How did you verify your code works?
TODO:
- [ ] new project
- [ ] existing project without configVersion
- [ ] existing project with configVersion
- [ ] same as above but with bun.lockb
- [ ] configVersion@0 defaults to hoisted linker
- [ ] new projects use isolated linker

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2025-11-03 22:20:07 -08:00

238 lines
7.0 KiB
TypeScript

import { file, spawn, write } from "bun";
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
import { exists } from "fs/promises";
import { VerdaccioRegistry, bunEnv, bunExe, runBunInstall, stderrForInstall } from "harness";
import { join } from "path";
var registry = new VerdaccioRegistry();
beforeAll(async () => {
await registry.start();
});
afterAll(() => {
registry.stop();
});
describe("basic", () => {
async function createBasicCatalogMonorepo(packageDir: string, name: string, inTopLevelKey: boolean = false) {
const catalogs = {
catalog: {
"no-deps": "2.0.0",
},
catalogs: {
a: {
"a-dep": "1.0.1",
},
},
};
const packageJson = !inTopLevelKey
? {
name,
workspaces: {
packages: ["packages/*"],
...catalogs,
},
}
: {
name,
...catalogs,
workspaces: {
packages: ["packages/*"],
},
};
await Promise.all([
write(join(packageDir, "package.json"), JSON.stringify(packageJson)),
write(
join(packageDir, "packages", "pkg1", "package.json"),
JSON.stringify({
name: "pkg1",
dependencies: {
"no-deps": "catalog:",
"a-dep": "catalog:a",
},
}),
),
]);
return packageJson;
}
for (const isTopLevel of [true, false]) {
test(`both catalog and catalogs ${isTopLevel ? "in top-level" : "in workspaces"}`, async () => {
const { packageDir } = await registry.createTestDir();
await createBasicCatalogMonorepo(packageDir, "catalog-basic-1", isTopLevel);
await runBunInstall(bunEnv, packageDir);
expect(await file(join(packageDir, "node_modules", "no-deps", "package.json")).json()).toEqual({
name: "no-deps",
version: "2.0.0",
});
expect(await file(join(packageDir, "node_modules", "a-dep", "package.json")).json()).toEqual({
name: "a-dep",
version: "1.0.1",
});
// another install does not save the lockfile
await runBunInstall(bunEnv, packageDir, { savesLockfile: false });
});
}
for (const binaryLockfile of [true, false]) {
test(`detect changes (${binaryLockfile ? "bun.lockb" : "bun.lock"})`, async () => {
const { packageDir } = await registry.createTestDir({
bunfigOpts: { saveTextLockfile: !binaryLockfile, linker: "hoisted" },
});
const packageJson = await createBasicCatalogMonorepo(packageDir, "catalog-basic-2");
let { err } = await runBunInstall(bunEnv, packageDir);
expect(err).toContain("Saved lockfile");
const initialLockfile = !binaryLockfile
? (await file(join(packageDir, "bun.lock")).text()).replaceAll(/localhost:\d+/g, "localhost:1234")
: undefined;
if (!binaryLockfile) {
expect(initialLockfile).toMatchSnapshot();
} else {
expect(await exists(join(packageDir, "bun.lockb"))).toBeTrue();
}
expect(await file(join(packageDir, "node_modules", "no-deps", "package.json")).json()).toEqual({
name: "no-deps",
version: "2.0.0",
});
expect(await file(join(packageDir, "node_modules", "a-dep", "package.json")).json()).toEqual({
name: "a-dep",
version: "1.0.1",
});
// update catalog
packageJson.workspaces.catalog["no-deps"] = "1.0.0";
await write(join(packageDir, "package.json"), JSON.stringify(packageJson));
({ err } = await runBunInstall(bunEnv, packageDir, { savesLockfile: true }));
expect(err).toContain("Saved lockfile");
if (!binaryLockfile) {
const newLockfile = (await file(join(packageDir, "bun.lock")).text()).replaceAll(
/localhost:\d+/g,
"localhost:1234",
);
expect(newLockfile).not.toEqual(initialLockfile);
expect(newLockfile).toMatchSnapshot();
} else {
expect(await exists(join(packageDir, "bun.lockb"))).toBeTrue();
}
expect(await file(join(packageDir, "node_modules", "no-deps", "package.json")).json()).toEqual({
name: "no-deps",
version: "1.0.0",
});
expect(await file(join(packageDir, "node_modules", "a-dep", "package.json")).json()).toEqual({
name: "a-dep",
version: "1.0.1",
});
// update catalogs
packageJson.workspaces!.catalogs!.a["a-dep"] = "1.0.10";
await write(join(packageDir, "package.json"), JSON.stringify(packageJson));
({ err } = await runBunInstall(bunEnv, packageDir, { savesLockfile: true }));
expect(err).toContain("Saved lockfile");
if (!binaryLockfile) {
const newLockfile = (await file(join(packageDir, "bun.lock")).text()).replaceAll(
/localhost:\d+/g,
"localhost:1234",
);
expect(newLockfile).not.toEqual(initialLockfile);
expect(newLockfile).toMatchSnapshot();
} else {
expect(await exists(join(packageDir, "bun.lockb"))).toBeTrue();
}
expect(await file(join(packageDir, "node_modules", "no-deps", "package.json")).json()).toEqual({
name: "no-deps",
version: "1.0.0",
});
expect(await file(join(packageDir, "node_modules", "a-dep", "package.json")).json()).toEqual({
name: "a-dep",
version: "1.0.10",
});
});
}
});
describe("errors", () => {
test("fails gracefully when no catalog is found for a package", async () => {
const { packageDir, packageJson } = await registry.createTestDir();
await write(
packageJson,
JSON.stringify({
name: "catalog-error-1",
workspaces: {
// empty, any catalog should fail to resolve
catalog: {},
catalogs: {},
},
dependencies: {
"no-deps": "catalog:",
// longer than 8
"a-dep": "catalog:aaaaaaaaaaaaaaaaa",
},
}),
);
const { stdout, stderr, exited } = spawn({
cmd: [bunExe(), "install"],
cwd: packageDir,
stdout: "pipe",
stderr: "pipe",
env: bunEnv,
});
const out = await stdout.text();
const err = stderrForInstall(await stderr.text());
expect(err).toContain("no-deps@catalog: failed to resolve");
expect(err).toContain("a-dep@catalog:aaaaaaaaaaaaaaaaa failed to resolve");
});
test("invalid dependency version", async () => {
const { packageDir, packageJson } = await registry.createTestDir();
await write(
packageJson,
JSON.stringify({
name: "catalog-error-2",
workspaces: {
catalog: {
"no-deps": ".:",
},
},
dependencies: {
"no-deps": "catalog:",
},
}),
);
const { stdout, stderr, exited } = spawn({
cmd: [bunExe(), "install"],
cwd: packageDir,
stdout: "pipe",
stderr: "pipe",
env: bunEnv,
});
const out = await stdout.text();
const err = stderrForInstall(await stderr.text());
expect(err).toContain("no-deps@catalog: failed to resolve");
});
});