Files
clawdbot/src/postinstall-patcher.test.ts
Peter Steinberger c379191f80 chore: migrate to oxlint and oxfmt
Co-authored-by: Christoph Nakazawa <christoph.pojer@gmail.com>
2026-01-14 15:02:19 +00:00

175 lines
5.5 KiB
TypeScript

import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { describe, expect, it } from "vitest";
import {
applyPatchSet,
detectPackageManager,
shouldApplyPnpmPatchedDependenciesFallback,
} from "../scripts/postinstall.js";
function makeTempDir() {
return fs.mkdtempSync(path.join(os.tmpdir(), "clawdbot-patch-"));
}
describe("postinstall patcher", () => {
it("detects package manager from user agent", () => {
expect(detectPackageManager("pnpm/10.0.0 npm/? node/v22.0.0")).toBe("pnpm");
expect(detectPackageManager("npm/10.9.0 node/v22.0.0")).toBe("npm");
expect(detectPackageManager("bun/1.2.2")).toBe("bun");
expect(detectPackageManager("yarn/4.0.0 npm/? node/v22.0.0")).toBe("yarn");
expect(detectPackageManager("")).toBe("unknown");
});
it("skips pnpm.patchedDependencies fallback for pnpm", () => {
expect(shouldApplyPnpmPatchedDependenciesFallback("pnpm")).toBe(false);
expect(shouldApplyPnpmPatchedDependenciesFallback("npm")).toBe(true);
expect(shouldApplyPnpmPatchedDependenciesFallback("bun")).toBe(true);
expect(shouldApplyPnpmPatchedDependenciesFallback("unknown")).toBe(true);
});
it("applies a simple patch", () => {
const dir = makeTempDir();
const target = path.join(dir, "lib");
fs.mkdirSync(target);
const filePath = path.join(target, "main.js");
const original = `${[
"var QRCode = require('./../vendor/QRCode'),",
" QRErrorCorrectLevel = require('./../vendor/QRCode/QRErrorCorrectLevel'),",
' black = "\\033[40m \\033[0m",',
' white = "\\033[47m \\033[0m",',
" toCell = function (isBlack) {",
].join("\n")}\n`;
fs.writeFileSync(filePath, original, "utf-8");
const patchText = `diff --git a/lib/main.js b/lib/main.js
index 0000000..1111111 100644
--- a/lib/main.js
+++ b/lib/main.js
@@ -1,5 +1,5 @@
-var QRCode = require('./../vendor/QRCode'),
- QRErrorCorrectLevel = require('./../vendor/QRCode/QRErrorCorrectLevel'),
+var QRCode = require('./../vendor/QRCode/index.js'),
+ QRErrorCorrectLevel = require('./../vendor/QRCode/QRErrorCorrectLevel.js'),
black = "\\033[40m \\033[0m",
white = "\\033[47m \\033[0m",
toCell = function (isBlack) {
`;
applyPatchSet({ patchText, targetDir: dir });
const updated = fs.readFileSync(filePath, "utf-8");
expect(updated).toBe(
`${[
"var QRCode = require('./../vendor/QRCode/index.js'),",
" QRErrorCorrectLevel = require('./../vendor/QRCode/QRErrorCorrectLevel.js'),",
' black = "\\033[40m \\033[0m",',
' white = "\\033[47m \\033[0m",',
" toCell = function (isBlack) {",
].join("\n")}\n`,
);
fs.rmSync(dir, { recursive: true, force: true });
});
it("treats already-applied hunks as no-ops", () => {
const dir = makeTempDir();
const target = path.join(dir, "lib");
fs.mkdirSync(target);
const filePath = path.join(target, "main.js");
const original = `${[
"var QRCode = require('./../vendor/QRCode'),",
" QRErrorCorrectLevel = require('./../vendor/QRCode/QRErrorCorrectLevel'),",
' black = "\\033[40m \\033[0m",',
' white = "\\033[47m \\033[0m",',
" toCell = function (isBlack) {",
].join("\n")}\n`;
fs.writeFileSync(filePath, original, "utf-8");
const patchText = `diff --git a/lib/main.js b/lib/main.js
index 0000000..1111111 100644
--- a/lib/main.js
+++ b/lib/main.js
@@ -1,5 +1,5 @@
-var QRCode = require('./../vendor/QRCode'),
- QRErrorCorrectLevel = require('./../vendor/QRCode/QRErrorCorrectLevel'),
+var QRCode = require('./../vendor/QRCode/index.js'),
+ QRErrorCorrectLevel = require('./../vendor/QRCode/QRErrorCorrectLevel.js'),
black = "\\033[40m \\033[0m",
white = "\\033[47m \\033[0m",
toCell = function (isBlack) {
`;
applyPatchSet({ patchText, targetDir: dir });
applyPatchSet({ patchText, targetDir: dir });
const updated = fs.readFileSync(filePath, "utf-8");
expect(updated).toBe(
`${[
"var QRCode = require('./../vendor/QRCode/index.js'),",
" QRErrorCorrectLevel = require('./../vendor/QRCode/QRErrorCorrectLevel.js'),",
' black = "\\033[40m \\033[0m",',
' white = "\\033[47m \\033[0m",',
" toCell = function (isBlack) {",
].join("\n")}\n`,
);
fs.rmSync(dir, { recursive: true, force: true });
});
it("handles multiple hunks with offsets", () => {
const dir = makeTempDir();
const filePath = path.join(dir, "file.txt");
fs.writeFileSync(
filePath,
`${["alpha", "beta", "gamma", "delta", "epsilon"].join("\n")}\n`,
"utf-8",
);
const patchText = `diff --git a/file.txt b/file.txt
--- a/file.txt
+++ b/file.txt
@@ -1,3 +1,4 @@
alpha
beta
+beta2
gamma
@@ -3,3 +4,4 @@
gamma
-delta
+DELTA
epsilon
+zeta
`;
applyPatchSet({ patchText, targetDir: dir });
const updated = fs.readFileSync(filePath, "utf-8").trim().split("\n");
expect(updated).toEqual(["alpha", "beta", "beta2", "gamma", "DELTA", "epsilon", "zeta"]);
fs.rmSync(dir, { recursive: true, force: true });
});
it("throws on context mismatch", () => {
const dir = makeTempDir();
const filePath = path.join(dir, "file.txt");
fs.writeFileSync(filePath, "hello\nworld\n", "utf-8");
const patchText = `diff --git a/file.txt b/file.txt
--- a/file.txt
+++ b/file.txt
@@ -1,2 +1,2 @@
-hola
+hi
world
`;
expect(() => applyPatchSet({ patchText, targetDir: dir })).toThrow();
fs.rmSync(dir, { recursive: true, force: true });
});
});