refactor: dedupe media and request-body test scaffolding
This commit is contained in:
@@ -5,9 +5,9 @@ import sharp from "sharp";
|
||||
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest";
|
||||
import { resolveStateDir } from "../config/paths.js";
|
||||
import { sendVoiceMessageDiscord } from "../discord/send.js";
|
||||
import * as ssrf from "../infra/net/ssrf.js";
|
||||
import { resolvePreferredOpenClawTmpDir } from "../infra/tmp-openclaw-dir.js";
|
||||
import { optimizeImageToPng } from "../media/image-ops.js";
|
||||
import { mockPinnedHostnameResolution } from "../test-helpers/ssrf.js";
|
||||
import { captureEnv } from "../test-utils/env.js";
|
||||
import {
|
||||
LocalMediaAccessError,
|
||||
@@ -126,15 +126,7 @@ describe("web media loading", () => {
|
||||
});
|
||||
|
||||
beforeAll(() => {
|
||||
vi.spyOn(ssrf, "resolvePinnedHostname").mockImplementation(async (hostname) => {
|
||||
const normalized = hostname.trim().toLowerCase().replace(/\.$/, "");
|
||||
const addresses = ["93.184.216.34"];
|
||||
return {
|
||||
hostname: normalized,
|
||||
addresses,
|
||||
lookup: ssrf.createPinnedLookup({ hostname: normalized, addresses }),
|
||||
};
|
||||
});
|
||||
mockPinnedHostnameResolution();
|
||||
});
|
||||
|
||||
it("strips MEDIA: prefix before reading local file (including whitespace variants)", async () => {
|
||||
@@ -240,6 +232,18 @@ describe("web media loading", () => {
|
||||
fetchMock.mockRestore();
|
||||
});
|
||||
|
||||
it("keeps raw mode when options object sets optimizeImages true", async () => {
|
||||
const { buffer, file } = await createLargeTestJpeg();
|
||||
const cap = Math.max(1, Math.floor(buffer.length * 0.8));
|
||||
|
||||
await expect(
|
||||
loadWebMediaRaw(file, {
|
||||
maxBytes: cap,
|
||||
optimizeImages: true,
|
||||
}),
|
||||
).rejects.toThrow(/Media exceeds/i);
|
||||
});
|
||||
|
||||
it("uses content-disposition filename when available", async () => {
|
||||
const fetchMock = vi.spyOn(globalThis, "fetch").mockResolvedValueOnce({
|
||||
ok: true,
|
||||
|
||||
@@ -34,6 +34,27 @@ type WebMediaOptions = {
|
||||
readFile?: (filePath: string) => Promise<Buffer>;
|
||||
};
|
||||
|
||||
function resolveWebMediaOptions(params: {
|
||||
maxBytesOrOptions?: number | WebMediaOptions;
|
||||
options?: { ssrfPolicy?: SsrFPolicy; localRoots?: readonly string[] | "any" };
|
||||
optimizeImages: boolean;
|
||||
}): WebMediaOptions {
|
||||
if (typeof params.maxBytesOrOptions === "number" || params.maxBytesOrOptions === undefined) {
|
||||
return {
|
||||
maxBytes: params.maxBytesOrOptions,
|
||||
optimizeImages: params.optimizeImages,
|
||||
ssrfPolicy: params.options?.ssrfPolicy,
|
||||
localRoots: params.options?.localRoots,
|
||||
};
|
||||
}
|
||||
return {
|
||||
...params.maxBytesOrOptions,
|
||||
optimizeImages: params.optimizeImages
|
||||
? (params.maxBytesOrOptions.optimizeImages ?? true)
|
||||
: false,
|
||||
};
|
||||
}
|
||||
|
||||
export type LocalMediaAccessErrorCode =
|
||||
| "path-not-allowed"
|
||||
| "invalid-root"
|
||||
@@ -385,18 +406,10 @@ export async function loadWebMedia(
|
||||
maxBytesOrOptions?: number | WebMediaOptions,
|
||||
options?: { ssrfPolicy?: SsrFPolicy; localRoots?: readonly string[] | "any" },
|
||||
): Promise<WebMediaResult> {
|
||||
if (typeof maxBytesOrOptions === "number" || maxBytesOrOptions === undefined) {
|
||||
return await loadWebMediaInternal(mediaUrl, {
|
||||
maxBytes: maxBytesOrOptions,
|
||||
optimizeImages: true,
|
||||
ssrfPolicy: options?.ssrfPolicy,
|
||||
localRoots: options?.localRoots,
|
||||
});
|
||||
}
|
||||
return await loadWebMediaInternal(mediaUrl, {
|
||||
...maxBytesOrOptions,
|
||||
optimizeImages: maxBytesOrOptions.optimizeImages ?? true,
|
||||
});
|
||||
return await loadWebMediaInternal(
|
||||
mediaUrl,
|
||||
resolveWebMediaOptions({ maxBytesOrOptions, options, optimizeImages: true }),
|
||||
);
|
||||
}
|
||||
|
||||
export async function loadWebMediaRaw(
|
||||
@@ -404,18 +417,10 @@ export async function loadWebMediaRaw(
|
||||
maxBytesOrOptions?: number | WebMediaOptions,
|
||||
options?: { ssrfPolicy?: SsrFPolicy; localRoots?: readonly string[] | "any" },
|
||||
): Promise<WebMediaResult> {
|
||||
if (typeof maxBytesOrOptions === "number" || maxBytesOrOptions === undefined) {
|
||||
return await loadWebMediaInternal(mediaUrl, {
|
||||
maxBytes: maxBytesOrOptions,
|
||||
optimizeImages: false,
|
||||
ssrfPolicy: options?.ssrfPolicy,
|
||||
localRoots: options?.localRoots,
|
||||
});
|
||||
}
|
||||
return await loadWebMediaInternal(mediaUrl, {
|
||||
...maxBytesOrOptions,
|
||||
optimizeImages: false,
|
||||
});
|
||||
return await loadWebMediaInternal(
|
||||
mediaUrl,
|
||||
resolveWebMediaOptions({ maxBytesOrOptions, options, optimizeImages: false }),
|
||||
);
|
||||
}
|
||||
|
||||
export async function optimizeImageToJpeg(
|
||||
|
||||
Reference in New Issue
Block a user