From 407f7017ec16dfb11b7f3e1c20268370f6b69355 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 22 Feb 2026 15:26:34 +0000 Subject: [PATCH] test: cache plugin install archive fixtures --- src/plugins/install.test.ts | 99 +++++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 42 deletions(-) diff --git a/src/plugins/install.test.ts b/src/plugins/install.test.ts index 4bb235497bb..5841d9c8f8d 100644 --- a/src/plugins/install.test.ts +++ b/src/plugins/install.test.ts @@ -4,7 +4,7 @@ import os from "node:os"; import path from "node:path"; import JSZip from "jszip"; import * as tar from "tar"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import * as skillScanner from "../security/skill-scanner.js"; import { expectSingleNpmInstallIgnoreScriptsCall, @@ -93,14 +93,55 @@ async function createVoiceCallArchive(params: { return { pkgDir, archivePath }; } -async function setupVoiceCallArchiveInstall(params: { outName: string; version: string }) { - const stateDir = makeTempDir(); +async function createVoiceCallArchiveBuffer(version: string): Promise { const workDir = makeTempDir(); const { archivePath } = await createVoiceCallArchive({ workDir, - outName: params.outName, - version: params.version, + outName: `plugin-${version}.tgz`, + version, }); + return fs.readFileSync(archivePath); +} + +function writeArchiveBuffer(params: { outName: string; buffer: Buffer }): string { + const workDir = makeTempDir(); + const archivePath = path.join(workDir, params.outName); + fs.writeFileSync(archivePath, params.buffer); + return archivePath; +} + +async function createZipperArchiveBuffer(): Promise { + const zip = new JSZip(); + zip.file( + "package/package.json", + JSON.stringify({ + name: "@openclaw/zipper", + version: "0.0.1", + openclaw: { extensions: ["./dist/index.js"] }, + }), + ); + zip.file("package/dist/index.js", "export {};"); + return zip.generateAsync({ type: "nodebuffer" }); +} + +const VOICE_CALL_ARCHIVE_V1_BUFFER_PROMISE = createVoiceCallArchiveBuffer("0.0.1"); +const VOICE_CALL_ARCHIVE_V2_BUFFER_PROMISE = createVoiceCallArchiveBuffer("0.0.2"); +const ZIPPER_ARCHIVE_BUFFER_PROMISE = createZipperArchiveBuffer(); + +async function getVoiceCallArchiveBuffer(version: string): Promise { + if (version === "0.0.1") { + return VOICE_CALL_ARCHIVE_V1_BUFFER_PROMISE; + } + if (version === "0.0.2") { + return VOICE_CALL_ARCHIVE_V2_BUFFER_PROMISE; + } + return createVoiceCallArchiveBuffer(version); +} + +async function setupVoiceCallArchiveInstall(params: { outName: string; version: string }) { + const stateDir = makeTempDir(); + const archiveBuffer = await getVoiceCallArchiveBuffer(params.version); + const archivePath = writeArchiveBuffer({ outName: params.outName, buffer: archiveBuffer }); return { stateDir, archivePath, @@ -174,7 +215,7 @@ async function expectArchiveInstallReservedSegmentRejection(params: { expect(result.error).toContain("reserved path segment"); } -afterEach(() => { +afterAll(() => { for (const dir of tempDirs.splice(0)) { try { fs.rmSync(dir, { recursive: true, force: true }); @@ -238,21 +279,10 @@ describe("installPluginFromArchive", () => { it("installs from a zip archive", async () => { const stateDir = makeTempDir(); - const workDir = makeTempDir(); - const archivePath = path.join(workDir, "plugin.zip"); - - const zip = new JSZip(); - zip.file( - "package/package.json", - JSON.stringify({ - name: "@openclaw/zipper", - version: "0.0.1", - openclaw: { extensions: ["./dist/index.js"] }, - }), - ); - zip.file("package/dist/index.js", "export {};"); - const buffer = await zip.generateAsync({ type: "nodebuffer" }); - fs.writeFileSync(archivePath, buffer); + const archivePath = writeArchiveBuffer({ + outName: "plugin.zip", + buffer: await ZIPPER_ARCHIVE_BUFFER_PROMISE, + }); const extensionsDir = path.join(stateDir, "extensions"); const result = await installPluginFromArchive({ @@ -270,16 +300,13 @@ describe("installPluginFromArchive", () => { it("allows updates when mode is update", async () => { const stateDir = makeTempDir(); - const workDir = makeTempDir(); - const { archivePath: archiveV1 } = await createVoiceCallArchive({ - workDir, + const archiveV1 = writeArchiveBuffer({ outName: "plugin-v1.tgz", - version: "0.0.1", + buffer: await VOICE_CALL_ARCHIVE_V1_BUFFER_PROMISE, }); - const { archivePath: archiveV2 } = await createVoiceCallArchive({ - workDir, + const archiveV2 = writeArchiveBuffer({ outName: "plugin-v2.tgz", - version: "0.0.2", + buffer: await VOICE_CALL_ARCHIVE_V2_BUFFER_PROMISE, }); const extensionsDir = path.join(stateDir, "extensions"); @@ -463,32 +490,20 @@ describe("installPluginFromDir", () => { describe("installPluginFromNpmSpec", () => { it("uses --ignore-scripts for npm pack and cleans up temp dir", async () => { - const workDir = makeTempDir(); const stateDir = makeTempDir(); - const pkgDir = path.join(workDir, "package"); - fs.mkdirSync(path.join(pkgDir, "dist"), { recursive: true }); - fs.writeFileSync( - path.join(pkgDir, "package.json"), - JSON.stringify({ - name: "@openclaw/voice-call", - version: "0.0.1", - openclaw: { extensions: ["./dist/index.js"] }, - }), - "utf-8", - ); - fs.writeFileSync(path.join(pkgDir, "dist", "index.js"), "export {};", "utf-8"); const extensionsDir = path.join(stateDir, "extensions"); fs.mkdirSync(extensionsDir, { recursive: true }); const run = vi.mocked(runCommandWithTimeout); + const voiceCallArchiveBuffer = await VOICE_CALL_ARCHIVE_V1_BUFFER_PROMISE; let packTmpDir = ""; const packedName = "voice-call-0.0.1.tgz"; run.mockImplementation(async (argv, opts) => { if (argv[0] === "npm" && argv[1] === "pack") { packTmpDir = String(typeof opts === "number" ? "" : (opts.cwd ?? "")); - await packToArchive({ pkgDir, outDir: packTmpDir, outName: packedName }); + fs.writeFileSync(path.join(packTmpDir, packedName), voiceCallArchiveBuffer); return { code: 0, stdout: JSON.stringify([