mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-19 01:04:45 +00:00
test: clear clawhub broad matchers
This commit is contained in:
@@ -93,9 +93,10 @@ async function expectClawHubInstallError(params: {
|
||||
};
|
||||
}) {
|
||||
params.setup?.();
|
||||
await expect(installPluginFromClawHub({ spec: params.spec })).resolves.toMatchObject(
|
||||
params.expected,
|
||||
);
|
||||
const result = await installPluginFromClawHub({ spec: params.spec });
|
||||
const failure = expectInstallFailure(result);
|
||||
expect(failure.code).toBe(params.expected.code);
|
||||
expect(failure.error).toBe(params.expected.error);
|
||||
}
|
||||
|
||||
function createLoggerSpies() {
|
||||
@@ -135,44 +136,107 @@ function expectClawHubInstallFlow(params: {
|
||||
version: string;
|
||||
archivePath: string;
|
||||
}) {
|
||||
expect(fetchClawHubPackageDetailMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
name: "demo",
|
||||
baseUrl: params.baseUrl,
|
||||
}),
|
||||
);
|
||||
expect(fetchClawHubPackageVersionMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
name: "demo",
|
||||
version: params.version,
|
||||
}),
|
||||
);
|
||||
expect(fetchClawHubPackageArtifactMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
name: "demo",
|
||||
version: params.version,
|
||||
}),
|
||||
);
|
||||
expect(installPluginFromArchiveMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
archivePath: params.archivePath,
|
||||
}),
|
||||
);
|
||||
expect(packageDetailCall().name).toBe("demo");
|
||||
expect(packageDetailCall().baseUrl).toBe(params.baseUrl);
|
||||
expect(packageVersionCall().name).toBe("demo");
|
||||
expect(packageVersionCall().version).toBe(params.version);
|
||||
expect(packageArtifactCall().name).toBe("demo");
|
||||
expect(packageArtifactCall().version).toBe(params.version);
|
||||
expect(archiveInstallCall().archivePath).toBe(params.archivePath);
|
||||
}
|
||||
|
||||
function expectSuccessfulClawHubInstall(result: unknown) {
|
||||
expect(result).toMatchObject({
|
||||
ok: true,
|
||||
pluginId: "demo",
|
||||
version: "2026.3.22",
|
||||
clawhub: {
|
||||
source: "clawhub",
|
||||
clawhubPackage: "demo",
|
||||
clawhubFamily: "code-plugin",
|
||||
clawhubChannel: "official",
|
||||
integrity: DEMO_ARCHIVE_INTEGRITY,
|
||||
},
|
||||
});
|
||||
const success = expectInstallSuccess(result);
|
||||
expect(success.pluginId).toBe("demo");
|
||||
expect(success.version).toBe("2026.3.22");
|
||||
expect(success.clawhub?.source).toBe("clawhub");
|
||||
expect(success.clawhub?.clawhubPackage).toBe("demo");
|
||||
expect(success.clawhub?.clawhubFamily).toBe("code-plugin");
|
||||
expect(success.clawhub?.clawhubChannel).toBe("official");
|
||||
expect(success.clawhub?.integrity).toBe(DEMO_ARCHIVE_INTEGRITY);
|
||||
}
|
||||
|
||||
type MockWithCalls = {
|
||||
mock: {
|
||||
calls: readonly (readonly unknown[])[];
|
||||
};
|
||||
};
|
||||
|
||||
type PackageLookupCall = {
|
||||
artifact?: string;
|
||||
baseUrl?: string;
|
||||
name?: string;
|
||||
version?: string;
|
||||
};
|
||||
|
||||
type ArchiveInstallCall = {
|
||||
archivePath?: string;
|
||||
dangerouslyForceUnsafeInstall?: boolean;
|
||||
trustedSourceLinkedOfficialInstall?: boolean;
|
||||
};
|
||||
|
||||
type InstallSuccess = {
|
||||
clawhub?: Record<string, unknown>;
|
||||
ok: true;
|
||||
pluginId?: string;
|
||||
version?: string;
|
||||
};
|
||||
|
||||
type InstallFailure = {
|
||||
code?: string;
|
||||
error: string;
|
||||
ok: false;
|
||||
};
|
||||
|
||||
function mockCallArg(mock: MockWithCalls, callIndex = 0, argIndex = 0): unknown {
|
||||
const call = mock.mock.calls[callIndex];
|
||||
if (!call) {
|
||||
throw new Error(`Expected mock call ${callIndex}`);
|
||||
}
|
||||
if (call.length <= argIndex) {
|
||||
throw new Error(`Expected mock call ${callIndex} argument ${argIndex}`);
|
||||
}
|
||||
return call[argIndex];
|
||||
}
|
||||
|
||||
function packageDetailCall(callIndex = 0): PackageLookupCall {
|
||||
return mockCallArg(fetchClawHubPackageDetailMock, callIndex) as PackageLookupCall;
|
||||
}
|
||||
|
||||
function packageVersionCall(callIndex = 0): PackageLookupCall {
|
||||
return mockCallArg(fetchClawHubPackageVersionMock, callIndex) as PackageLookupCall;
|
||||
}
|
||||
|
||||
function packageArtifactCall(callIndex = 0): PackageLookupCall {
|
||||
return mockCallArg(fetchClawHubPackageArtifactMock, callIndex) as PackageLookupCall;
|
||||
}
|
||||
|
||||
function archiveDownloadCall(callIndex = 0): PackageLookupCall {
|
||||
return mockCallArg(downloadClawHubPackageArchiveMock, callIndex) as PackageLookupCall;
|
||||
}
|
||||
|
||||
function archiveInstallCall(callIndex = 0): ArchiveInstallCall {
|
||||
return mockCallArg(installPluginFromArchiveMock, callIndex) as ArchiveInstallCall;
|
||||
}
|
||||
|
||||
function expectInstallSuccess(result: unknown): InstallSuccess {
|
||||
expect((result as { ok?: unknown }).ok).toBe(true);
|
||||
return result as InstallSuccess;
|
||||
}
|
||||
|
||||
function expectInstallFailure(result: unknown): InstallFailure {
|
||||
expect((result as { ok?: unknown }).ok).toBe(false);
|
||||
return result as InstallFailure;
|
||||
}
|
||||
|
||||
function expectInstallFailureFields(
|
||||
result: unknown,
|
||||
code: (typeof CLAWHUB_INSTALL_ERROR_CODE)[keyof typeof CLAWHUB_INSTALL_ERROR_CODE],
|
||||
error: string,
|
||||
) {
|
||||
const failure = expectInstallFailure(result);
|
||||
expect(failure.code).toBe(code);
|
||||
expect(failure.error).toBe(error);
|
||||
}
|
||||
|
||||
describe("installPluginFromClawHub", () => {
|
||||
@@ -289,11 +353,7 @@ describe("installPluginFromClawHub", () => {
|
||||
baseUrl: "https://clawhub.ai",
|
||||
});
|
||||
|
||||
expect(installPluginFromArchiveMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
trustedSourceLinkedOfficialInstall: true,
|
||||
}),
|
||||
);
|
||||
expect(archiveInstallCall().trustedSourceLinkedOfficialInstall).toBe(true);
|
||||
});
|
||||
|
||||
it("resolves explicit ClawHub dist tags before fetching version metadata", async () => {
|
||||
@@ -323,18 +383,10 @@ describe("installPluginFromClawHub", () => {
|
||||
});
|
||||
|
||||
expectSuccessfulClawHubInstall(result);
|
||||
expect(fetchClawHubPackageVersionMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
name: "demo",
|
||||
version: "2026.3.22",
|
||||
}),
|
||||
);
|
||||
expect(downloadClawHubPackageArchiveMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
name: "demo",
|
||||
version: "2026.3.22",
|
||||
}),
|
||||
);
|
||||
expect(packageVersionCall().name).toBe("demo");
|
||||
expect(packageVersionCall().version).toBe("2026.3.22");
|
||||
expect(archiveDownloadCall().name).toBe("demo");
|
||||
expect(archiveDownloadCall().version).toBe("2026.3.22");
|
||||
});
|
||||
|
||||
it("returns ClawPack metadata from compatible ClawHub package versions", async () => {
|
||||
@@ -376,26 +428,18 @@ describe("installPluginFromClawHub", () => {
|
||||
baseUrl: "https://clawhub.ai",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: true,
|
||||
clawhub: {
|
||||
integrity: DEMO_CLAWPACK_INTEGRITY,
|
||||
artifactKind: "npm-pack",
|
||||
artifactFormat: "tgz",
|
||||
npmIntegrity: "sha512-clawpack",
|
||||
npmShasum: "1".repeat(40),
|
||||
npmTarballName: "demo-2026.3.22.tgz",
|
||||
clawpackSha256: DEMO_CLAWPACK_SHA256,
|
||||
clawpackSize: 4096,
|
||||
},
|
||||
});
|
||||
expect(downloadClawHubPackageArchiveMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
artifact: "clawpack",
|
||||
name: "demo",
|
||||
version: "2026.3.22",
|
||||
}),
|
||||
);
|
||||
const success = expectInstallSuccess(result);
|
||||
expect(success.clawhub?.integrity).toBe(DEMO_CLAWPACK_INTEGRITY);
|
||||
expect(success.clawhub?.artifactKind).toBe("npm-pack");
|
||||
expect(success.clawhub?.artifactFormat).toBe("tgz");
|
||||
expect(success.clawhub?.npmIntegrity).toBe("sha512-clawpack");
|
||||
expect(success.clawhub?.npmShasum).toBe("1".repeat(40));
|
||||
expect(success.clawhub?.npmTarballName).toBe("demo-2026.3.22.tgz");
|
||||
expect(success.clawhub?.clawpackSha256).toBe(DEMO_CLAWPACK_SHA256);
|
||||
expect(success.clawhub?.clawpackSize).toBe(4096);
|
||||
expect(archiveDownloadCall().artifact).toBe("clawpack");
|
||||
expect(archiveDownloadCall().name).toBe("demo");
|
||||
expect(archiveDownloadCall().version).toBe("2026.3.22");
|
||||
});
|
||||
|
||||
it("uses the artifact resolver response as the install decision", async () => {
|
||||
@@ -439,30 +483,18 @@ describe("installPluginFromClawHub", () => {
|
||||
baseUrl: "https://clawhub.ai",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: true,
|
||||
clawhub: {
|
||||
artifactKind: "npm-pack",
|
||||
artifactFormat: "tgz",
|
||||
npmIntegrity: "sha512-clawpack",
|
||||
npmShasum: "1".repeat(40),
|
||||
clawpackSha256: DEMO_CLAWPACK_SHA256,
|
||||
},
|
||||
});
|
||||
expect(fetchClawHubPackageArtifactMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
name: "demo",
|
||||
version: "2026.3.22",
|
||||
}),
|
||||
);
|
||||
const success = expectInstallSuccess(result);
|
||||
expect(success.clawhub?.artifactKind).toBe("npm-pack");
|
||||
expect(success.clawhub?.artifactFormat).toBe("tgz");
|
||||
expect(success.clawhub?.npmIntegrity).toBe("sha512-clawpack");
|
||||
expect(success.clawhub?.npmShasum).toBe("1".repeat(40));
|
||||
expect(success.clawhub?.clawpackSha256).toBe(DEMO_CLAWPACK_SHA256);
|
||||
expect(packageArtifactCall().name).toBe("demo");
|
||||
expect(packageArtifactCall().version).toBe("2026.3.22");
|
||||
expect(fetchClawHubPackageVersionMock).not.toHaveBeenCalled();
|
||||
expect(downloadClawHubPackageArchiveMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
artifact: "clawpack",
|
||||
name: "demo",
|
||||
version: "2026.3.22",
|
||||
}),
|
||||
);
|
||||
expect(archiveDownloadCall().artifact).toBe("clawpack");
|
||||
expect(archiveDownloadCall().name).toBe("demo");
|
||||
expect(archiveDownloadCall().version).toBe("2026.3.22");
|
||||
});
|
||||
|
||||
it("accepts the live ClawHub artifact resolver shape with kind/sha256 field names", async () => {
|
||||
@@ -497,24 +529,16 @@ describe("installPluginFromClawHub", () => {
|
||||
baseUrl: "https://clawhub.ai",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: true,
|
||||
clawhub: {
|
||||
artifactKind: "npm-pack",
|
||||
artifactFormat: "tgz",
|
||||
npmIntegrity: "sha512-clawpack",
|
||||
npmShasum: "1".repeat(40),
|
||||
clawpackSha256: DEMO_CLAWPACK_SHA256,
|
||||
},
|
||||
});
|
||||
const success = expectInstallSuccess(result);
|
||||
expect(success.clawhub?.artifactKind).toBe("npm-pack");
|
||||
expect(success.clawhub?.artifactFormat).toBe("tgz");
|
||||
expect(success.clawhub?.npmIntegrity).toBe("sha512-clawpack");
|
||||
expect(success.clawhub?.npmShasum).toBe("1".repeat(40));
|
||||
expect(success.clawhub?.clawpackSha256).toBe(DEMO_CLAWPACK_SHA256);
|
||||
expect(fetchClawHubPackageVersionMock).not.toHaveBeenCalled();
|
||||
expect(downloadClawHubPackageArchiveMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
artifact: "clawpack",
|
||||
name: "demo",
|
||||
version: "2026.3.22",
|
||||
}),
|
||||
);
|
||||
expect(archiveDownloadCall().artifact).toBe("clawpack");
|
||||
expect(archiveDownloadCall().name).toBe("demo");
|
||||
expect(archiveDownloadCall().version).toBe("2026.3.22");
|
||||
});
|
||||
|
||||
it("accepts the live ClawHub legacy zip resolver shape with kind/sha256 field names", async () => {
|
||||
@@ -542,23 +566,15 @@ describe("installPluginFromClawHub", () => {
|
||||
baseUrl: "https://clawhub.ai",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: true,
|
||||
pluginId: "demo",
|
||||
clawhub: {
|
||||
artifactKind: "legacy-zip",
|
||||
artifactFormat: "zip",
|
||||
integrity: DEMO_ARCHIVE_INTEGRITY,
|
||||
},
|
||||
});
|
||||
const success = expectInstallSuccess(result);
|
||||
expect(success.pluginId).toBe("demo");
|
||||
expect(success.clawhub?.artifactKind).toBe("legacy-zip");
|
||||
expect(success.clawhub?.artifactFormat).toBe("zip");
|
||||
expect(success.clawhub?.integrity).toBe(DEMO_ARCHIVE_INTEGRITY);
|
||||
expect(fetchClawHubPackageVersionMock).not.toHaveBeenCalled();
|
||||
expect(downloadClawHubPackageArchiveMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
artifact: "archive",
|
||||
name: "demo",
|
||||
version: "2026.3.22",
|
||||
}),
|
||||
);
|
||||
expect(archiveDownloadCall().artifact).toBe("archive");
|
||||
expect(archiveDownloadCall().name).toBe("demo");
|
||||
expect(archiveDownloadCall().version).toBe("2026.3.22");
|
||||
});
|
||||
|
||||
it("falls back to version metadata when the ClawHub artifact resolver route is missing", async () => {
|
||||
@@ -609,27 +625,15 @@ describe("installPluginFromClawHub", () => {
|
||||
baseUrl: "https://clawhub.ai",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: true,
|
||||
clawhub: {
|
||||
artifactKind: "npm-pack",
|
||||
npmIntegrity: "sha512-clawpack",
|
||||
clawpackSha256: DEMO_CLAWPACK_SHA256,
|
||||
},
|
||||
});
|
||||
expect(fetchClawHubPackageVersionMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
name: "demo",
|
||||
version: "2026.3.22",
|
||||
}),
|
||||
);
|
||||
expect(downloadClawHubPackageArchiveMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
artifact: "clawpack",
|
||||
name: "demo",
|
||||
version: "2026.3.22",
|
||||
}),
|
||||
);
|
||||
const success = expectInstallSuccess(result);
|
||||
expect(success.clawhub?.artifactKind).toBe("npm-pack");
|
||||
expect(success.clawhub?.npmIntegrity).toBe("sha512-clawpack");
|
||||
expect(success.clawhub?.clawpackSha256).toBe(DEMO_CLAWPACK_SHA256);
|
||||
expect(packageVersionCall().name).toBe("demo");
|
||||
expect(packageVersionCall().version).toBe("2026.3.22");
|
||||
expect(archiveDownloadCall().artifact).toBe("clawpack");
|
||||
expect(archiveDownloadCall().name).toBe("demo");
|
||||
expect(archiveDownloadCall().version).toBe("2026.3.22");
|
||||
});
|
||||
|
||||
it("installs ClawPack artifacts when version metadata has no legacy archive hash", async () => {
|
||||
@@ -664,23 +668,11 @@ describe("installPluginFromClawHub", () => {
|
||||
baseUrl: "https://clawhub.ai",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: true,
|
||||
clawhub: {
|
||||
integrity: DEMO_CLAWPACK_INTEGRITY,
|
||||
clawpackSha256: DEMO_CLAWPACK_SHA256,
|
||||
},
|
||||
});
|
||||
expect(downloadClawHubPackageArchiveMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
artifact: "clawpack",
|
||||
}),
|
||||
);
|
||||
expect(installPluginFromArchiveMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
archivePath: "/tmp/clawhub-demo/demo-2026.3.22.tgz",
|
||||
}),
|
||||
);
|
||||
const success = expectInstallSuccess(result);
|
||||
expect(success.clawhub?.integrity).toBe(DEMO_CLAWPACK_INTEGRITY);
|
||||
expect(success.clawhub?.clawpackSha256).toBe(DEMO_CLAWPACK_SHA256);
|
||||
expect(archiveDownloadCall().artifact).toBe("clawpack");
|
||||
expect(archiveInstallCall().archivePath).toBe("/tmp/clawhub-demo/demo-2026.3.22.tgz");
|
||||
});
|
||||
|
||||
it("rejects ClawPack artifacts when the download digest does not match version metadata", async () => {
|
||||
@@ -715,11 +707,11 @@ describe("installPluginFromClawHub", () => {
|
||||
baseUrl: "https://clawhub.ai",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
code: CLAWHUB_INSTALL_ERROR_CODE.ARCHIVE_INTEGRITY_MISMATCH,
|
||||
error: `ClawHub ClawPack integrity mismatch for "demo@2026.3.22": expected ${DEMO_CLAWPACK_SHA256}, got ${mismatchedSha256}.`,
|
||||
});
|
||||
const failure = expectInstallFailure(result);
|
||||
expect(failure.code).toBe(CLAWHUB_INSTALL_ERROR_CODE.ARCHIVE_INTEGRITY_MISMATCH);
|
||||
expect(failure.error).toBe(
|
||||
`ClawHub ClawPack integrity mismatch for "demo@2026.3.22": expected ${DEMO_CLAWPACK_SHA256}, got ${mismatchedSha256}.`,
|
||||
);
|
||||
expect(installPluginFromArchiveMock).not.toHaveBeenCalled();
|
||||
expect(archiveCleanupMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
@@ -754,16 +746,11 @@ describe("installPluginFromClawHub", () => {
|
||||
baseUrl: "https://clawhub.ai",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
error:
|
||||
'ClawHub artifact download for "demo@2026.3.22" is not available yet (ClawHub /api/v1/packages/demo/versions/2026.3.22/artifact/download failed (404): Not Found). Use "npm:demo@2026.3.22" for launch installs while ClawHub artifact routing is being rolled out.',
|
||||
});
|
||||
expect(downloadClawHubPackageArchiveMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
artifact: "clawpack",
|
||||
}),
|
||||
const failure = expectInstallFailure(result);
|
||||
expect(failure.error).toBe(
|
||||
'ClawHub artifact download for "demo@2026.3.22" is not available yet (ClawHub /api/v1/packages/demo/versions/2026.3.22/artifact/download failed (404): Not Found). Use "npm:demo@2026.3.22" for launch installs while ClawHub artifact routing is being rolled out.',
|
||||
);
|
||||
expect(archiveDownloadCall().artifact).toBe("clawpack");
|
||||
expect(installPluginFromArchiveMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -808,19 +795,12 @@ describe("installPluginFromClawHub", () => {
|
||||
baseUrl: "https://clawhub.ai",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: true,
|
||||
clawhub: {
|
||||
source: "clawhub",
|
||||
},
|
||||
});
|
||||
if (!result.ok) {
|
||||
throw new Error(result.error);
|
||||
}
|
||||
expect(result.clawhub.clawpackSha256).toBeUndefined();
|
||||
expect(result.clawhub.clawpackSpecVersion).toBeUndefined();
|
||||
expect(result.clawhub.clawpackManifestSha256).toBeUndefined();
|
||||
expect(result.clawhub.clawpackSize).toBeUndefined();
|
||||
const success = expectInstallSuccess(result);
|
||||
expect(success.clawhub?.source).toBe("clawhub");
|
||||
expect(success.clawhub?.clawpackSha256).toBeUndefined();
|
||||
expect(success.clawhub?.clawpackSpecVersion).toBeUndefined();
|
||||
expect(success.clawhub?.clawpackManifestSha256).toBeUndefined();
|
||||
expect(success.clawhub?.clawpackSize).toBeUndefined();
|
||||
});
|
||||
|
||||
it("installs when ClawHub advertises a wildcard plugin API range", async () => {
|
||||
@@ -844,11 +824,7 @@ describe("installPluginFromClawHub", () => {
|
||||
|
||||
expectSuccessfulClawHubInstall(result);
|
||||
expect(downloadClawHubPackageArchiveMock).toHaveBeenCalledTimes(1);
|
||||
expect(installPluginFromArchiveMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
archivePath: "/tmp/clawhub-demo/archive.zip",
|
||||
}),
|
||||
);
|
||||
expect(archiveInstallCall().archivePath).toBe("/tmp/clawhub-demo/archive.zip");
|
||||
expect(archiveCleanupMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
@@ -874,11 +850,7 @@ describe("installPluginFromClawHub", () => {
|
||||
|
||||
expectSuccessfulClawHubInstall(result);
|
||||
expect(downloadClawHubPackageArchiveMock).toHaveBeenCalledTimes(1);
|
||||
expect(installPluginFromArchiveMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
archivePath: "/tmp/clawhub-demo/archive.zip",
|
||||
}),
|
||||
);
|
||||
expect(archiveInstallCall().archivePath).toBe("/tmp/clawhub-demo/archive.zip");
|
||||
expect(archiveCleanupMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
@@ -901,11 +873,11 @@ describe("installPluginFromClawHub", () => {
|
||||
spec: "clawhub:demo",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
code: CLAWHUB_INSTALL_ERROR_CODE.INCOMPATIBLE_PLUGIN_API,
|
||||
error: 'Plugin "demo" requires plugin API *, but this OpenClaw runtime exposes invalid.',
|
||||
});
|
||||
const failure = expectInstallFailure(result);
|
||||
expect(failure.code).toBe(CLAWHUB_INSTALL_ERROR_CODE.INCOMPATIBLE_PLUGIN_API);
|
||||
expect(failure.error).toBe(
|
||||
'Plugin "demo" requires plugin API *, but this OpenClaw runtime exposes invalid.',
|
||||
);
|
||||
expect(downloadClawHubPackageArchiveMock).not.toHaveBeenCalled();
|
||||
expect(installPluginFromArchiveMock).not.toHaveBeenCalled();
|
||||
expect(archiveCleanupMock).not.toHaveBeenCalled();
|
||||
@@ -917,12 +889,8 @@ describe("installPluginFromClawHub", () => {
|
||||
dangerouslyForceUnsafeInstall: true,
|
||||
});
|
||||
|
||||
expect(installPluginFromArchiveMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
archivePath: "/tmp/clawhub-demo/archive.zip",
|
||||
dangerouslyForceUnsafeInstall: true,
|
||||
}),
|
||||
);
|
||||
expect(archiveInstallCall().archivePath).toBe("/tmp/clawhub-demo/archive.zip");
|
||||
expect(archiveInstallCall().dangerouslyForceUnsafeInstall).toBe(true);
|
||||
});
|
||||
|
||||
it("cleans up the downloaded archive even when archive install fails", async () => {
|
||||
@@ -936,10 +904,7 @@ describe("installPluginFromClawHub", () => {
|
||||
baseUrl: "https://clawhub.ai",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
error: "bad archive",
|
||||
});
|
||||
expect(expectInstallFailure(result).error).toBe("bad archive");
|
||||
expect(archiveCleanupMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
@@ -966,7 +931,8 @@ describe("installPluginFromClawHub", () => {
|
||||
spec: "clawhub:demo",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({ ok: true, pluginId: "demo" });
|
||||
const success = expectInstallSuccess(result);
|
||||
expect(success.pluginId).toBe("demo");
|
||||
});
|
||||
|
||||
it("accepts version-endpoint SHA-256 hashes expressed as unpadded SRI", async () => {
|
||||
@@ -992,7 +958,8 @@ describe("installPluginFromClawHub", () => {
|
||||
spec: "clawhub:demo",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({ ok: true, pluginId: "demo" });
|
||||
const success = expectInstallSuccess(result);
|
||||
expect(success.pluginId).toBe("demo");
|
||||
});
|
||||
|
||||
it("falls back to strict files[] verification when sha256hash is missing", async () => {
|
||||
@@ -1036,7 +1003,8 @@ describe("installPluginFromClawHub", () => {
|
||||
logger,
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({ ok: true, pluginId: "demo" });
|
||||
const success = expectInstallSuccess(result);
|
||||
expect(success.pluginId).toBe("demo");
|
||||
expect(logger.warn).toHaveBeenCalledWith(
|
||||
'ClawHub package "demo@2026.3.22" is missing sha256hash; falling back to files[] verification. Validated files: dist/index.js, openclaw.plugin.json. Validated generated metadata files present in archive: _meta.json (JSON parse plus slug/version match only).',
|
||||
);
|
||||
@@ -1093,18 +1061,12 @@ describe("installPluginFromClawHub", () => {
|
||||
logger,
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({ ok: true, pluginId: "demo", version: "2026.3.22" });
|
||||
expect(fetchClawHubPackageDetailMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
name: "DemoAlias",
|
||||
}),
|
||||
);
|
||||
expect(fetchClawHubPackageVersionMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
name: "demo",
|
||||
version: "latest",
|
||||
}),
|
||||
);
|
||||
const success = expectInstallSuccess(result);
|
||||
expect(success.pluginId).toBe("demo");
|
||||
expect(success.version).toBe("2026.3.22");
|
||||
expect(packageDetailCall().name).toBe("DemoAlias");
|
||||
expect(packageVersionCall().name).toBe("demo");
|
||||
expect(packageVersionCall().version).toBe("latest");
|
||||
expect(logger.warn).toHaveBeenCalledWith(
|
||||
'ClawHub package "demo@2026.3.22" is missing sha256hash; falling back to files[] verification. Validated files: openclaw.plugin.json. Validated generated metadata files present in archive: _meta.json (JSON parse plus slug/version match only).',
|
||||
);
|
||||
@@ -1135,12 +1097,11 @@ describe("installPluginFromClawHub", () => {
|
||||
spec: "clawhub:demo",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
code: CLAWHUB_INSTALL_ERROR_CODE.MISSING_ARCHIVE_INTEGRITY,
|
||||
error:
|
||||
'ClawHub version metadata for "demo@2026.3.22" has an invalid sha256hash (unrecognized value "definitely-not-a-sha256").',
|
||||
});
|
||||
const failure = expectInstallFailure(result);
|
||||
expect(failure.code).toBe(CLAWHUB_INSTALL_ERROR_CODE.MISSING_ARCHIVE_INTEGRITY);
|
||||
expect(failure.error).toBe(
|
||||
'ClawHub version metadata for "demo@2026.3.22" has an invalid sha256hash (unrecognized value "definitely-not-a-sha256").',
|
||||
);
|
||||
expect(downloadClawHubPackageArchiveMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -1162,12 +1123,11 @@ describe("installPluginFromClawHub", () => {
|
||||
spec: "clawhub:demo",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
code: CLAWHUB_INSTALL_ERROR_CODE.MISSING_ARCHIVE_INTEGRITY,
|
||||
error:
|
||||
'ClawHub package "demo@2026.3.22" does not expose a downloadable plugin artifact yet. Use "npm:demo@2026.3.22" for launch installs while ClawHub artifact routing is being rolled out.',
|
||||
});
|
||||
const failure = expectInstallFailure(result);
|
||||
expect(failure.code).toBe(CLAWHUB_INSTALL_ERROR_CODE.MISSING_ARCHIVE_INTEGRITY);
|
||||
expect(failure.error).toBe(
|
||||
'ClawHub package "demo@2026.3.22" does not expose a downloadable plugin artifact yet. Use "npm:demo@2026.3.22" for launch installs while ClawHub artifact routing is being rolled out.',
|
||||
);
|
||||
expect(downloadClawHubPackageArchiveMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -1188,12 +1148,11 @@ describe("installPluginFromClawHub", () => {
|
||||
spec: "clawhub:demo",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
code: CLAWHUB_INSTALL_ERROR_CODE.MISSING_ARCHIVE_INTEGRITY,
|
||||
error:
|
||||
'ClawHub package "demo@2026.3.22" does not expose a downloadable plugin artifact yet. Use "npm:demo@2026.3.22" for launch installs while ClawHub artifact routing is being rolled out.',
|
||||
});
|
||||
const failure = expectInstallFailure(result);
|
||||
expect(failure.code).toBe(CLAWHUB_INSTALL_ERROR_CODE.MISSING_ARCHIVE_INTEGRITY);
|
||||
expect(failure.error).toBe(
|
||||
'ClawHub package "demo@2026.3.22" does not expose a downloadable plugin artifact yet. Use "npm:demo@2026.3.22" for launch installs while ClawHub artifact routing is being rolled out.',
|
||||
);
|
||||
expect(downloadClawHubPackageArchiveMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -1215,12 +1174,11 @@ describe("installPluginFromClawHub", () => {
|
||||
spec: "clawhub:demo",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
code: CLAWHUB_INSTALL_ERROR_CODE.MISSING_ARCHIVE_INTEGRITY,
|
||||
error:
|
||||
'ClawHub version metadata for "demo@2026.3.22" has an invalid files[0] entry (expected an object, got null).',
|
||||
});
|
||||
const failure = expectInstallFailure(result);
|
||||
expect(failure.code).toBe(CLAWHUB_INSTALL_ERROR_CODE.MISSING_ARCHIVE_INTEGRITY);
|
||||
expect(failure.error).toBe(
|
||||
'ClawHub version metadata for "demo@2026.3.22" has an invalid files[0] entry (expected an object, got null).',
|
||||
);
|
||||
expect(downloadClawHubPackageArchiveMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -1248,12 +1206,11 @@ describe("installPluginFromClawHub", () => {
|
||||
spec: "clawhub:demo",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
code: CLAWHUB_INSTALL_ERROR_CODE.MISSING_ARCHIVE_INTEGRITY,
|
||||
error:
|
||||
'ClawHub version metadata for "demo@2026.3.22" has an invalid files[0].sha256 (value "not-a-digest" is not a 64-character hexadecimal SHA-256 digest).',
|
||||
});
|
||||
expectInstallFailureFields(
|
||||
result,
|
||||
CLAWHUB_INSTALL_ERROR_CODE.MISSING_ARCHIVE_INTEGRITY,
|
||||
'ClawHub version metadata for "demo@2026.3.22" has an invalid files[0].sha256 (value "not-a-digest" is not a 64-character hexadecimal SHA-256 digest).',
|
||||
);
|
||||
expect(downloadClawHubPackageArchiveMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -1275,12 +1232,11 @@ describe("installPluginFromClawHub", () => {
|
||||
spec: "clawhub:demo",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
code: CLAWHUB_INSTALL_ERROR_CODE.MISSING_ARCHIVE_INTEGRITY,
|
||||
error:
|
||||
'ClawHub version metadata for "demo@2026.3.22" has an invalid sha256hash (non-string value of type number).',
|
||||
});
|
||||
expectInstallFailureFields(
|
||||
result,
|
||||
CLAWHUB_INSTALL_ERROR_CODE.MISSING_ARCHIVE_INTEGRITY,
|
||||
'ClawHub version metadata for "demo@2026.3.22" has an invalid sha256hash (non-string value of type number).',
|
||||
);
|
||||
expect(downloadClawHubPackageArchiveMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -1291,10 +1247,7 @@ describe("installPluginFromClawHub", () => {
|
||||
spec: "clawhub:demo",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
error: "network timeout",
|
||||
});
|
||||
expect(expectInstallFailure(result).error).toBe("network timeout");
|
||||
expect(installPluginFromArchiveMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -1331,11 +1284,11 @@ describe("installPluginFromClawHub", () => {
|
||||
spec: "clawhub:demo",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
code: CLAWHUB_INSTALL_ERROR_CODE.ARCHIVE_INTEGRITY_MISMATCH,
|
||||
error: "ClawHub archive fallback verification failed while reading the downloaded archive.",
|
||||
});
|
||||
expectInstallFailureFields(
|
||||
result,
|
||||
CLAWHUB_INSTALL_ERROR_CODE.ARCHIVE_INTEGRITY_MISMATCH,
|
||||
"ClawHub archive fallback verification failed while reading the downloaded archive.",
|
||||
);
|
||||
expect(installPluginFromArchiveMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -1362,11 +1315,11 @@ describe("installPluginFromClawHub", () => {
|
||||
spec: "clawhub:demo",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
code: CLAWHUB_INSTALL_ERROR_CODE.ARCHIVE_INTEGRITY_MISMATCH,
|
||||
error: `ClawHub archive integrity mismatch for "demo@2026.3.22": expected sha256-ERERERERERERERERERERERERERERERERERERERERERE=, got ${DEMO_ARCHIVE_INTEGRITY}.`,
|
||||
});
|
||||
expectInstallFailureFields(
|
||||
result,
|
||||
CLAWHUB_INSTALL_ERROR_CODE.ARCHIVE_INTEGRITY_MISMATCH,
|
||||
`ClawHub archive integrity mismatch for "demo@2026.3.22": expected sha256-ERERERERERERERERERERERERERERERERERERERERERE=, got ${DEMO_ARCHIVE_INTEGRITY}.`,
|
||||
);
|
||||
expect(installPluginFromArchiveMock).not.toHaveBeenCalled();
|
||||
expect(archiveCleanupMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
@@ -1407,12 +1360,11 @@ describe("installPluginFromClawHub", () => {
|
||||
spec: "clawhub:demo",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
code: CLAWHUB_INSTALL_ERROR_CODE.ARCHIVE_INTEGRITY_MISMATCH,
|
||||
error:
|
||||
'ClawHub archive contents do not match files[] metadata for "demo@2026.3.22": missing "dist/index.js".',
|
||||
});
|
||||
expectInstallFailureFields(
|
||||
result,
|
||||
CLAWHUB_INSTALL_ERROR_CODE.ARCHIVE_INTEGRITY_MISMATCH,
|
||||
'ClawHub archive contents do not match files[] metadata for "demo@2026.3.22": missing "dist/index.js".',
|
||||
);
|
||||
expect(installPluginFromArchiveMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -1454,12 +1406,11 @@ describe("installPluginFromClawHub", () => {
|
||||
spec: "clawhub:demo",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
code: CLAWHUB_INSTALL_ERROR_CODE.ARCHIVE_INTEGRITY_MISMATCH,
|
||||
error:
|
||||
'ClawHub archive contents do not match files[] metadata for "demo@2026.3.22": unexpected file "extra.txt".',
|
||||
});
|
||||
expectInstallFailureFields(
|
||||
result,
|
||||
CLAWHUB_INSTALL_ERROR_CODE.ARCHIVE_INTEGRITY_MISMATCH,
|
||||
'ClawHub archive contents do not match files[] metadata for "demo@2026.3.22": unexpected file "extra.txt".',
|
||||
);
|
||||
expect(installPluginFromArchiveMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -1508,7 +1459,7 @@ describe("installPluginFromClawHub", () => {
|
||||
logger,
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({ ok: true, pluginId: "demo" });
|
||||
expect(expectInstallSuccess(result).pluginId).toBe("demo");
|
||||
expect(logger.warn).toHaveBeenCalledWith(
|
||||
'ClawHub package "demo@2026.3.22" is missing sha256hash; falling back to files[] verification. Validated files: SKILL.md, scripts/search.py. Validated generated metadata files present in archive: _meta.json (JSON parse plus slug/version match only).',
|
||||
);
|
||||
@@ -1547,7 +1498,7 @@ describe("installPluginFromClawHub", () => {
|
||||
logger,
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({ ok: true, pluginId: "demo" });
|
||||
expect(expectInstallSuccess(result).pluginId).toBe("demo");
|
||||
expect(logger.warn).toHaveBeenCalledWith(
|
||||
'ClawHub package "demo@2026.3.22" is missing sha256hash; falling back to files[] verification. Validated files: openclaw.plugin.json.',
|
||||
);
|
||||
@@ -1585,12 +1536,11 @@ describe("installPluginFromClawHub", () => {
|
||||
spec: "clawhub:demo",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
code: CLAWHUB_INSTALL_ERROR_CODE.ARCHIVE_INTEGRITY_MISMATCH,
|
||||
error:
|
||||
'ClawHub archive contents do not match files[] metadata for "demo@2026.3.22": _meta.json is not valid JSON.',
|
||||
});
|
||||
expectInstallFailureFields(
|
||||
result,
|
||||
CLAWHUB_INSTALL_ERROR_CODE.ARCHIVE_INTEGRITY_MISMATCH,
|
||||
'ClawHub archive contents do not match files[] metadata for "demo@2026.3.22": _meta.json is not valid JSON.',
|
||||
);
|
||||
expect(installPluginFromArchiveMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -1626,12 +1576,11 @@ describe("installPluginFromClawHub", () => {
|
||||
spec: "clawhub:demo",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
code: CLAWHUB_INSTALL_ERROR_CODE.ARCHIVE_INTEGRITY_MISMATCH,
|
||||
error:
|
||||
'ClawHub archive contents do not match files[] metadata for "demo@2026.3.22": _meta.json slug does not match the package name.',
|
||||
});
|
||||
expectInstallFailureFields(
|
||||
result,
|
||||
CLAWHUB_INSTALL_ERROR_CODE.ARCHIVE_INTEGRITY_MISMATCH,
|
||||
'ClawHub archive contents do not match files[] metadata for "demo@2026.3.22": _meta.json slug does not match the package name.',
|
||||
);
|
||||
expect(installPluginFromArchiveMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -1687,12 +1636,11 @@ describe("installPluginFromClawHub", () => {
|
||||
});
|
||||
|
||||
loadAsyncSpy.mockRestore();
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
code: CLAWHUB_INSTALL_ERROR_CODE.ARCHIVE_INTEGRITY_MISMATCH,
|
||||
error:
|
||||
'ClawHub archive fallback verification rejected "_meta.json" because it exceeds the per-file size limit.',
|
||||
});
|
||||
expectInstallFailureFields(
|
||||
result,
|
||||
CLAWHUB_INSTALL_ERROR_CODE.ARCHIVE_INTEGRITY_MISMATCH,
|
||||
'ClawHub archive fallback verification rejected "_meta.json" because it exceeds the per-file size limit.',
|
||||
);
|
||||
expect(installPluginFromArchiveMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -1742,11 +1690,11 @@ describe("installPluginFromClawHub", () => {
|
||||
});
|
||||
|
||||
loadAsyncSpy.mockRestore();
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
code: CLAWHUB_INSTALL_ERROR_CODE.ARCHIVE_INTEGRITY_MISMATCH,
|
||||
error: "ClawHub archive fallback verification exceeded the archive entry limit.",
|
||||
});
|
||||
expectInstallFailureFields(
|
||||
result,
|
||||
CLAWHUB_INSTALL_ERROR_CODE.ARCHIVE_INTEGRITY_MISMATCH,
|
||||
"ClawHub archive fallback verification exceeded the archive entry limit.",
|
||||
);
|
||||
expect(installPluginFromArchiveMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -1792,11 +1740,11 @@ describe("installPluginFromClawHub", () => {
|
||||
});
|
||||
|
||||
loadAsyncSpy.mockRestore();
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
code: CLAWHUB_INSTALL_ERROR_CODE.ARCHIVE_INTEGRITY_MISMATCH,
|
||||
error: "ClawHub archive fallback verification exceeded the archive entry limit.",
|
||||
});
|
||||
expectInstallFailureFields(
|
||||
result,
|
||||
CLAWHUB_INSTALL_ERROR_CODE.ARCHIVE_INTEGRITY_MISMATCH,
|
||||
"ClawHub archive fallback verification exceeded the archive entry limit.",
|
||||
);
|
||||
expect(loadAsyncSpy).not.toHaveBeenCalled();
|
||||
expect(installPluginFromArchiveMock).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -1844,12 +1792,11 @@ describe("installPluginFromClawHub", () => {
|
||||
});
|
||||
|
||||
statSpy.mockRestore();
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
code: CLAWHUB_INSTALL_ERROR_CODE.ARCHIVE_INTEGRITY_MISMATCH,
|
||||
error:
|
||||
"ClawHub archive fallback verification rejected the downloaded archive because it exceeds the ZIP archive size limit.",
|
||||
});
|
||||
expectInstallFailureFields(
|
||||
result,
|
||||
CLAWHUB_INSTALL_ERROR_CODE.ARCHIVE_INTEGRITY_MISMATCH,
|
||||
"ClawHub archive fallback verification rejected the downloaded archive because it exceeds the ZIP archive size limit.",
|
||||
);
|
||||
expect(installPluginFromArchiveMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -1884,11 +1831,11 @@ describe("installPluginFromClawHub", () => {
|
||||
spec: "clawhub:demo",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
code: CLAWHUB_INSTALL_ERROR_CODE.ARCHIVE_INTEGRITY_MISMATCH,
|
||||
error: `ClawHub archive contents do not match files[] metadata for "demo@2026.3.22": expected openclaw.plugin.json to hash to ${"1".repeat(64)}, got ${sha256Hex('{"id":"demo"}')}.`,
|
||||
});
|
||||
expectInstallFailureFields(
|
||||
result,
|
||||
CLAWHUB_INSTALL_ERROR_CODE.ARCHIVE_INTEGRITY_MISMATCH,
|
||||
`ClawHub archive contents do not match files[] metadata for "demo@2026.3.22": expected openclaw.plugin.json to hash to ${"1".repeat(64)}, got ${sha256Hex('{"id":"demo"}')}.`,
|
||||
);
|
||||
expect(installPluginFromArchiveMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -1916,12 +1863,11 @@ describe("installPluginFromClawHub", () => {
|
||||
spec: "clawhub:demo",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
code: CLAWHUB_INSTALL_ERROR_CODE.MISSING_ARCHIVE_INTEGRITY,
|
||||
error:
|
||||
'ClawHub version metadata for "demo@2026.3.22" has an invalid files[0].path (path "../evil.txt" contains dot segments).',
|
||||
});
|
||||
expectInstallFailureFields(
|
||||
result,
|
||||
CLAWHUB_INSTALL_ERROR_CODE.MISSING_ARCHIVE_INTEGRITY,
|
||||
'ClawHub version metadata for "demo@2026.3.22" has an invalid files[0].path (path "../evil.txt" contains dot segments).',
|
||||
);
|
||||
expect(downloadClawHubPackageArchiveMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -1949,12 +1895,11 @@ describe("installPluginFromClawHub", () => {
|
||||
spec: "clawhub:demo",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
code: CLAWHUB_INSTALL_ERROR_CODE.MISSING_ARCHIVE_INTEGRITY,
|
||||
error:
|
||||
'ClawHub version metadata for "demo@2026.3.22" has an invalid files[0].path (path "openclaw.plugin.json " has leading or trailing whitespace).',
|
||||
});
|
||||
expectInstallFailureFields(
|
||||
result,
|
||||
CLAWHUB_INSTALL_ERROR_CODE.MISSING_ARCHIVE_INTEGRITY,
|
||||
'ClawHub version metadata for "demo@2026.3.22" has an invalid files[0].path (path "openclaw.plugin.json " has leading or trailing whitespace).',
|
||||
);
|
||||
expect(downloadClawHubPackageArchiveMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -1990,12 +1935,11 @@ describe("installPluginFromClawHub", () => {
|
||||
spec: "clawhub:demo",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
code: CLAWHUB_INSTALL_ERROR_CODE.ARCHIVE_INTEGRITY_MISMATCH,
|
||||
error:
|
||||
'ClawHub archive contents do not match files[] metadata for "demo@2026.3.22": invalid package file path "openclaw.plugin.json " (path "openclaw.plugin.json " has leading or trailing whitespace).',
|
||||
});
|
||||
expectInstallFailureFields(
|
||||
result,
|
||||
CLAWHUB_INSTALL_ERROR_CODE.ARCHIVE_INTEGRITY_MISMATCH,
|
||||
'ClawHub archive contents do not match files[] metadata for "demo@2026.3.22": invalid package file path "openclaw.plugin.json " (path "openclaw.plugin.json " has leading or trailing whitespace).',
|
||||
);
|
||||
expect(installPluginFromArchiveMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -2028,12 +1972,11 @@ describe("installPluginFromClawHub", () => {
|
||||
spec: "clawhub:demo",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
code: CLAWHUB_INSTALL_ERROR_CODE.MISSING_ARCHIVE_INTEGRITY,
|
||||
error:
|
||||
'ClawHub version metadata for "demo@2026.3.22" has duplicate files[] path "openclaw.plugin.json".',
|
||||
});
|
||||
expectInstallFailureFields(
|
||||
result,
|
||||
CLAWHUB_INSTALL_ERROR_CODE.MISSING_ARCHIVE_INTEGRITY,
|
||||
'ClawHub version metadata for "demo@2026.3.22" has duplicate files[] path "openclaw.plugin.json".',
|
||||
);
|
||||
expect(downloadClawHubPackageArchiveMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -2061,12 +2004,11 @@ describe("installPluginFromClawHub", () => {
|
||||
spec: "clawhub:demo",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
code: CLAWHUB_INSTALL_ERROR_CODE.MISSING_ARCHIVE_INTEGRITY,
|
||||
error:
|
||||
'ClawHub version metadata for "demo@2026.3.22" must not include generated file "_meta.json" in files[].',
|
||||
});
|
||||
expectInstallFailureFields(
|
||||
result,
|
||||
CLAWHUB_INSTALL_ERROR_CODE.MISSING_ARCHIVE_INTEGRITY,
|
||||
'ClawHub version metadata for "demo@2026.3.22" must not include generated file "_meta.json" in files[].',
|
||||
);
|
||||
expect(downloadClawHubPackageArchiveMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user