mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 13:50:49 +00:00
test: share bluebubbles media fixtures
This commit is contained in:
@@ -6,7 +6,6 @@ import {
|
||||
sendBlueBubblesAttachment,
|
||||
} from "./attachments.js";
|
||||
import { fetchBlueBubblesServerInfo, getCachedBlueBubblesPrivateApiStatus } from "./probe.js";
|
||||
import type { PluginRuntime } from "./runtime-api.js";
|
||||
import { setBlueBubblesRuntime } from "./runtime.js";
|
||||
import {
|
||||
BLUE_BUBBLES_PRIVATE_API_STATUS,
|
||||
@@ -14,53 +13,27 @@ import {
|
||||
mockBlueBubblesPrivateApiStatus,
|
||||
mockBlueBubblesPrivateApiStatusOnce,
|
||||
} from "./test-harness.js";
|
||||
import {
|
||||
createBlueBubblesFetchRemoteMediaMock,
|
||||
createBlueBubblesRuntimeStub,
|
||||
} from "./test-helpers.js";
|
||||
import type { BlueBubblesAttachment } from "./types.js";
|
||||
|
||||
const mockFetch = vi.fn();
|
||||
const fetchServerInfoMock = vi.mocked(fetchBlueBubblesServerInfo);
|
||||
const fetchRemoteMediaMock = vi.fn(
|
||||
async (params: {
|
||||
url: string;
|
||||
maxBytes?: number;
|
||||
fetchImpl?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
||||
}) => {
|
||||
const fetchFn = params.fetchImpl ?? fetch;
|
||||
const res = await fetchFn(params.url);
|
||||
if (!res.ok) {
|
||||
const text = await res.text().catch(() => "unknown");
|
||||
throw new Error(
|
||||
`Failed to fetch media from ${params.url}: HTTP ${res.status}; body: ${text}`,
|
||||
);
|
||||
}
|
||||
const buffer = Buffer.from(await res.arrayBuffer());
|
||||
if (typeof params.maxBytes === "number" && buffer.byteLength > params.maxBytes) {
|
||||
const error = new Error(`payload exceeds maxBytes ${params.maxBytes}`) as Error & {
|
||||
code?: string;
|
||||
};
|
||||
error.code = "max_bytes";
|
||||
throw error;
|
||||
}
|
||||
return {
|
||||
buffer,
|
||||
contentType: res.headers.get("content-type") ?? undefined,
|
||||
fileName: undefined,
|
||||
};
|
||||
const fetchRemoteMediaMock = createBlueBubblesFetchRemoteMediaMock({
|
||||
createHttpError: async ({ response, url }) => {
|
||||
const text = await response.text().catch(() => "unknown");
|
||||
return new Error(`Failed to fetch media from ${url}: HTTP ${response.status}; body: ${text}`);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
installBlueBubblesFetchTestHooks({
|
||||
mockFetch,
|
||||
privateApiStatusMock: vi.mocked(getCachedBlueBubblesPrivateApiStatus),
|
||||
});
|
||||
|
||||
const runtimeStub = {
|
||||
channel: {
|
||||
media: {
|
||||
fetchRemoteMedia:
|
||||
fetchRemoteMediaMock as unknown as PluginRuntime["channel"]["media"]["fetchRemoteMedia"],
|
||||
},
|
||||
},
|
||||
} as unknown as PluginRuntime;
|
||||
const runtimeStub = createBlueBubblesRuntimeStub(fetchRemoteMediaMock);
|
||||
|
||||
describe("downloadBlueBubblesAttachment", () => {
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { SsrFPolicy } from "openclaw/plugin-sdk/ssrf-runtime";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import "./test-mocks.js";
|
||||
import {
|
||||
@@ -11,12 +10,15 @@ import {
|
||||
resolveBlueBubblesClientSsrfPolicy,
|
||||
} from "./client.js";
|
||||
import { getCachedBlueBubblesPrivateApiStatus } from "./probe.js";
|
||||
import type { PluginRuntime } from "./runtime-api.js";
|
||||
import { setBlueBubblesRuntime } from "./runtime.js";
|
||||
import {
|
||||
createBlueBubblesFetchGuardPassthroughInstaller,
|
||||
installBlueBubblesFetchTestHooks,
|
||||
} from "./test-harness.js";
|
||||
import {
|
||||
createBlueBubblesFetchRemoteMediaMock,
|
||||
createBlueBubblesRuntimeStub,
|
||||
} from "./test-helpers.js";
|
||||
import type { BlueBubblesAttachment } from "./types.js";
|
||||
import { _setFetchGuardForTesting } from "./types.js";
|
||||
|
||||
@@ -24,47 +26,16 @@ import { _setFetchGuardForTesting } from "./types.js";
|
||||
|
||||
const mockFetch = vi.fn();
|
||||
|
||||
const fetchRemoteMediaMock = vi.fn(
|
||||
async (params: {
|
||||
url: string;
|
||||
maxBytes?: number;
|
||||
ssrfPolicy?: SsrFPolicy;
|
||||
fetchImpl?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
||||
}) => {
|
||||
const fetchFn = params.fetchImpl ?? fetch;
|
||||
const res = await fetchFn(params.url);
|
||||
if (!res.ok) {
|
||||
throw new Error(`media fetch failed: HTTP ${res.status}`);
|
||||
}
|
||||
const buffer = Buffer.from(await res.arrayBuffer());
|
||||
if (typeof params.maxBytes === "number" && buffer.byteLength > params.maxBytes) {
|
||||
const error = new Error(`payload exceeds maxBytes ${params.maxBytes}`) as Error & {
|
||||
code?: string;
|
||||
};
|
||||
error.code = "max_bytes";
|
||||
throw error;
|
||||
}
|
||||
return {
|
||||
buffer,
|
||||
contentType: res.headers.get("content-type") ?? undefined,
|
||||
fileName: undefined,
|
||||
};
|
||||
},
|
||||
);
|
||||
const fetchRemoteMediaMock = createBlueBubblesFetchRemoteMediaMock({
|
||||
createHttpError: ({ response }) => new Error(`media fetch failed: HTTP ${response.status}`),
|
||||
});
|
||||
|
||||
installBlueBubblesFetchTestHooks({
|
||||
mockFetch,
|
||||
privateApiStatusMock: vi.mocked(getCachedBlueBubblesPrivateApiStatus),
|
||||
});
|
||||
|
||||
const runtimeStub = {
|
||||
channel: {
|
||||
media: {
|
||||
fetchRemoteMedia:
|
||||
fetchRemoteMediaMock as unknown as PluginRuntime["channel"]["media"]["fetchRemoteMedia"],
|
||||
},
|
||||
},
|
||||
} as unknown as PluginRuntime;
|
||||
const runtimeStub = createBlueBubblesRuntimeStub(fetchRemoteMediaMock);
|
||||
|
||||
beforeEach(() => {
|
||||
fetchRemoteMediaMock.mockClear();
|
||||
|
||||
52
extensions/bluebubbles/src/test-helpers.ts
Normal file
52
extensions/bluebubbles/src/test-helpers.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { vi } from "vitest";
|
||||
import type { PluginRuntime } from "./runtime-api.js";
|
||||
|
||||
type FetchRemoteMediaParams = {
|
||||
url: string;
|
||||
maxBytes?: number;
|
||||
ssrfPolicy?: unknown;
|
||||
fetchImpl?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
||||
};
|
||||
|
||||
type FetchRemoteMediaHttpErrorParams = {
|
||||
response: Response;
|
||||
url: string;
|
||||
};
|
||||
|
||||
export function createBlueBubblesFetchRemoteMediaMock(options: {
|
||||
createHttpError: (params: FetchRemoteMediaHttpErrorParams) => Error | Promise<Error>;
|
||||
}) {
|
||||
return vi.fn(async (params: FetchRemoteMediaParams) => {
|
||||
const fetchFn = params.fetchImpl ?? fetch;
|
||||
const res = await fetchFn(params.url);
|
||||
if (!res.ok) {
|
||||
throw await options.createHttpError({ response: res, url: params.url });
|
||||
}
|
||||
const buffer = Buffer.from(await res.arrayBuffer());
|
||||
if (typeof params.maxBytes === "number" && buffer.byteLength > params.maxBytes) {
|
||||
const error = new Error(`payload exceeds maxBytes ${params.maxBytes}`) as Error & {
|
||||
code?: string;
|
||||
};
|
||||
error.code = "max_bytes";
|
||||
throw error;
|
||||
}
|
||||
return {
|
||||
buffer,
|
||||
contentType: res.headers.get("content-type") ?? undefined,
|
||||
fileName: undefined,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function createBlueBubblesRuntimeStub(
|
||||
fetchRemoteMediaMock: ReturnType<typeof createBlueBubblesFetchRemoteMediaMock>,
|
||||
) {
|
||||
return {
|
||||
channel: {
|
||||
media: {
|
||||
fetchRemoteMedia:
|
||||
fetchRemoteMediaMock as unknown as PluginRuntime["channel"]["media"]["fetchRemoteMedia"],
|
||||
},
|
||||
},
|
||||
} as unknown as PluginRuntime;
|
||||
}
|
||||
Reference in New Issue
Block a user