cache-apt-pkgs-action/test/packages.test.ts
awalsh128 64a160d1cc style: set Prettier print width to 110 and reformat
Without a repo config, the Prettier CLI and Trunk used the default
width of 80, while editors with a user printWidth reflowed files on
save, producing formatting-only diffs. Pin the width in .prettierrc.json
and reformat source, tests, scripts and JS/TS config files to match.

Files that already failed Prettier at width 80 for unrelated reasons
(tsconfig*.json, .trunk/*, V2_FAQ.MD) are left untouched.

No code changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 11:15:05 -08:00

185 lines
7.1 KiB
TypeScript

import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { createPackageName } from "ts-apt";
import { ActionPackageNames, updateAptLists, buildFileList, findInstallScript } from "../src/packages.js";
import { describe, expect, it, vi } from "vitest";
const PACKAGE_NAME = createPackageName("test-package", "1.0.0", "amd64");
function makeTempRoot(prefix: string): string {
return fs.mkdtempSync(path.join(os.tmpdir(), prefix));
}
function ensureScriptsDir(root: string): string {
const scriptsDir = path.join(root, "var", "lib", "dpkg", "info");
fs.mkdirSync(scriptsDir, { recursive: true });
return scriptsDir;
}
describe("packages", () => {
it("buildFileList returns an array of package files", async () => {
const root = makeTempRoot("pkg-files-");
const scriptsDir = ensureScriptsDir(root);
const packageFiles = [
path.join(scriptsDir, "test-package.list"),
path.join(scriptsDir, "test-package.md5sums"),
path.join(scriptsDir, "test-package.postinst"),
path.join(scriptsDir, "test-package.preinst"),
];
for (const filePath of packageFiles) {
fs.writeFileSync(filePath, "", "utf8");
}
const manager = {
listInstalledFiles: vi.fn().mockResolvedValue(packageFiles),
};
const result = await buildFileList(PACKAGE_NAME, manager as any, root);
expect(result).toEqual(packageFiles.map((item) => item.slice(1)).sort());
});
it("buildFileList returns an empty array when no files are found", async () => {
const root = makeTempRoot("pkg-empty-");
const manager = {
listInstalledFiles: vi.fn().mockResolvedValue([]),
};
const result = await buildFileList(PACKAGE_NAME, manager as any, root);
expect(result).toEqual([]);
});
it("findInstallScript returns undefined when scripts directory does not exist", async () => {
const missingRoot = makeTempRoot("pkg-missing-");
const result = await findInstallScript(PACKAGE_NAME, "preinst", missingRoot);
expect(result).toBeUndefined();
});
it("findInstallScript returns undefined when no matching scripts are found", async () => {
const root = makeTempRoot("pkg-no-match-");
const scriptsDir = ensureScriptsDir(root);
fs.writeFileSync(path.join(scriptsDir, "other-package.preinst"), "", "utf8");
const result = await findInstallScript(PACKAGE_NAME, "preinst", root);
expect(result).toBeUndefined();
});
it("findInstallScript returns the first matching script when multiple are found", async () => {
const root = makeTempRoot("pkg-match-");
const scriptsDir = ensureScriptsDir(root);
fs.writeFileSync(path.join(scriptsDir, "test-package.postinst"), "", "utf8");
fs.writeFileSync(path.join(scriptsDir, "test-package.preinst"), "", "utf8");
fs.writeFileSync(path.join(scriptsDir, "test-package.preinst.backup"), "", "utf8");
const result = await findInstallScript(PACKAGE_NAME, "preinst", root);
expect(result).toBe(path.join(scriptsDir, "test-package.preinst"));
});
it.each([
["g++", "g++.postinst"],
["libstdc++6", "libstdc++6:amd64.postinst"],
])(
"findInstallScript name %s with regex metacharacters returns its script",
async (name, scriptFilename) => {
const root = makeTempRoot("pkg-metachar-");
const scriptsDir = ensureScriptsDir(root);
fs.writeFileSync(path.join(scriptsDir, scriptFilename), "", "utf8");
const result = await findInstallScript(createPackageName(name), "postinst", root);
expect(result).toBe(path.join(scriptsDir, scriptFilename));
},
);
it("findInstallScript name with dot does not match a similarly named package", async () => {
const root = makeTempRoot("pkg-dot-");
const scriptsDir = ensureScriptsDir(root);
fs.writeFileSync(path.join(scriptsDir, "libaXb.postinst"), "", "utf8");
const result = await findInstallScript(createPackageName("liba.b"), "postinst", root);
expect(result).toBeUndefined();
});
it("updateAptLists calls the package manager's update method", async () => {
const aptListsPath = makeTempRoot("apt-lists-");
const mockUpdate = vi.fn().mockResolvedValue(undefined);
const mockLogger = {
info: vi.fn(),
error: vi.fn(),
};
const packageManager = {
update: mockUpdate,
};
await updateAptLists(packageManager as any, mockLogger as any, aptListsPath);
expect(mockUpdate).toHaveBeenCalledTimes(1);
});
describe("ActionPackageNames", () => {
it("fromInput parses APT serialized input", () => {
const input = "test-package=1.0.0 amd64 other-package=2.0.0 i386";
const result = ActionPackageNames.fromInput(input);
expect(result.toArray()).toEqual([
createPackageName("other-package", "2.0.0", "i386"),
createPackageName("test-package", "1.0.0", "amd64"),
]);
});
it("fromInput returns an empty list when input is empty", () => {
const result = ActionPackageNames.fromInput("");
expect(result.toArray()).toEqual([]);
});
it("fromInput returns an empty list when input is whitespace", () => {
const result = ActionPackageNames.fromInput(" ");
expect(result.toArray()).toEqual([]);
});
it("fromJSON parses an array of serialized package names", () => {
const json = [
{ name: "test-package", version: "1.0.0", arch: "amd64" },
{ name: "other-package", version: "2.0.0", arch: "i386" },
];
const result = ActionPackageNames.fromJSON(json);
expect(result.toArray()).toEqual([
createPackageName("other-package", "2.0.0", "i386"),
createPackageName("test-package", "1.0.0", "amd64"),
]);
});
it("fromJSON returns an empty list when input is empty", () => {
const result = ActionPackageNames.fromJSON([]);
expect(result.toArray()).toEqual([]);
});
it("length returns the number of package names", () => {
const input = "test-package=1.0.0 amd64 other-package=2.0.0 i386";
const result = ActionPackageNames.fromInput(input);
expect(result.length).toBe(2);
});
it("toArray returns a sorted array of package names", () => {
const input = "test-package=1.0.0 amd64 other-package=2.0.0 i386";
const result = ActionPackageNames.fromInput(input);
expect(result.toArray()).toEqual([
createPackageName("other-package", "2.0.0", "i386"),
createPackageName("test-package", "1.0.0", "amd64"),
]);
});
it.each([
["same name without arch returns true", "xdot", createPackageName("xdot", "1.3-1"), true],
["requested without arch returns true", "xdot", createPackageName("xdot", "1.3-1", "amd64"), true],
["installed without arch returns true", "xdot:amd64", createPackageName("xdot", "1.3-1"), true],
["matching arch returns true", "xdot:amd64", createPackageName("xdot", "1.3-1", "amd64"), true],
["different arch returns false", "xdot:i386", createPackageName("xdot", "1.3-1", "amd64"), false],
["different name returns false", "xdot", createPackageName("rolldice", "1.16"), false],
])("includes %s", (_case, input, installed, expected) => {
expect(ActionPackageNames.fromInput(input).includes(installed)).toBe(expected);
});
});
});