import { describe, expect, it, vi } from "vitest"; const validateRegistryNpmSpecMock = vi.hoisted(() => vi.fn()); const installFromNpmSpecArchiveWithInstallerMock = vi.hoisted(() => vi.fn()); const finalizeNpmSpecArchiveInstallMock = vi.hoisted(() => vi.fn()); vi.mock("./npm-registry-spec.js", () => ({ validateRegistryNpmSpec: (...args: unknown[]) => validateRegistryNpmSpecMock(...args), })); vi.mock("./npm-pack-install.js", () => ({ installFromNpmSpecArchiveWithInstaller: (...args: unknown[]) => installFromNpmSpecArchiveWithInstallerMock(...args), finalizeNpmSpecArchiveInstall: (...args: unknown[]) => finalizeNpmSpecArchiveInstallMock(...args), })); import { installFromValidatedNpmSpecArchive } from "./install-from-npm-spec.js"; describe("installFromValidatedNpmSpecArchive", () => { it("trims the spec and returns validation errors before running the installer", async () => { validateRegistryNpmSpecMock.mockReturnValueOnce("unsupported npm spec"); await expect( installFromValidatedNpmSpecArchive({ spec: " nope ", timeoutMs: 30_000, tempDirPrefix: "openclaw-npm-", installFromArchive: vi.fn(), archiveInstallParams: {}, }), ).resolves.toEqual({ ok: false, error: "unsupported npm spec" }); expect(validateRegistryNpmSpecMock).toHaveBeenCalledWith("nope"); expect(installFromNpmSpecArchiveWithInstallerMock).not.toHaveBeenCalled(); expect(finalizeNpmSpecArchiveInstallMock).not.toHaveBeenCalled(); }); it("passes the trimmed spec through the archive installer and finalizer", async () => { const installFromArchive = vi.fn(); const warn = vi.fn(); const onIntegrityDrift = vi.fn(); const flowResult = { ok: true, installResult: { ok: true }, npmResolution: { version: "1.2.3" }, }; const finalized = { ok: true, archivePath: "/tmp/pkg.tgz" }; validateRegistryNpmSpecMock.mockReturnValueOnce(null); installFromNpmSpecArchiveWithInstallerMock.mockResolvedValueOnce(flowResult); finalizeNpmSpecArchiveInstallMock.mockReturnValueOnce(finalized); await expect( installFromValidatedNpmSpecArchive({ spec: " @openclaw/demo@beta ", timeoutMs: 45_000, tempDirPrefix: "openclaw-npm-", expectedIntegrity: "sha512-demo", onIntegrityDrift, warn, installFromArchive, archiveInstallParams: { destination: "/tmp/demo" }, }), ).resolves.toBe(finalized); expect(installFromNpmSpecArchiveWithInstallerMock).toHaveBeenCalledWith({ tempDirPrefix: "openclaw-npm-", spec: "@openclaw/demo@beta", timeoutMs: 45_000, expectedIntegrity: "sha512-demo", onIntegrityDrift, warn, installFromArchive, archiveInstallParams: { destination: "/tmp/demo" }, }); expect(finalizeNpmSpecArchiveInstallMock).toHaveBeenCalledWith(flowResult); }); });