mirror of
https://github.com/farcasclaudiu/openclaw.git
synced 2026-06-28 19:01:47 +03:00
perf(test): reduce import and fixture overhead in hot tests
This commit is contained in:
+15
-23
@@ -1,4 +1,4 @@
|
|||||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
vi.mock("playwright-core", () => ({
|
vi.mock("playwright-core", () => ({
|
||||||
chromium: {
|
chromium: {
|
||||||
@@ -54,26 +54,28 @@ function createBrowser(pages: unknown[]) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function importModule() {
|
let mod: typeof import("./pw-ai.js");
|
||||||
return await import("./pw-ai.js");
|
let chromiumMock: typeof import("playwright-core").chromium;
|
||||||
}
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const pw = await import("playwright-core");
|
||||||
|
chromiumMock = pw.chromium;
|
||||||
|
mod = await import("./pw-ai.js");
|
||||||
|
});
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
const mod = await importModule();
|
|
||||||
await mod.closePlaywrightBrowserConnection();
|
await mod.closePlaywrightBrowserConnection();
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("pw-ai", () => {
|
describe("pw-ai", () => {
|
||||||
it("captures an ai snapshot via Playwright for a specific target", async () => {
|
it("captures an ai snapshot via Playwright for a specific target", async () => {
|
||||||
const { chromium } = await import("playwright-core");
|
|
||||||
const p1 = createPage({ targetId: "T1", snapshotFull: "ONE" });
|
const p1 = createPage({ targetId: "T1", snapshotFull: "ONE" });
|
||||||
const p2 = createPage({ targetId: "T2", snapshotFull: "TWO" });
|
const p2 = createPage({ targetId: "T2", snapshotFull: "TWO" });
|
||||||
const browser = createBrowser([p1.page, p2.page]);
|
const browser = createBrowser([p1.page, p2.page]);
|
||||||
|
|
||||||
(chromium.connectOverCDP as unknown as ReturnType<typeof vi.fn>).mockResolvedValue(browser);
|
(chromiumMock.connectOverCDP as unknown as ReturnType<typeof vi.fn>).mockResolvedValue(browser);
|
||||||
|
|
||||||
const mod = await importModule();
|
|
||||||
const res = await mod.snapshotAiViaPlaywright({
|
const res = await mod.snapshotAiViaPlaywright({
|
||||||
cdpUrl: "http://127.0.0.1:18792",
|
cdpUrl: "http://127.0.0.1:18792",
|
||||||
targetId: "T2",
|
targetId: "T2",
|
||||||
@@ -85,14 +87,12 @@ describe("pw-ai", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("registers aria refs from ai snapshots for act commands", async () => {
|
it("registers aria refs from ai snapshots for act commands", async () => {
|
||||||
const { chromium } = await import("playwright-core");
|
|
||||||
const snapshot = ['- button "OK" [ref=e1]', '- link "Docs" [ref=e2]'].join("\n");
|
const snapshot = ['- button "OK" [ref=e1]', '- link "Docs" [ref=e2]'].join("\n");
|
||||||
const p1 = createPage({ targetId: "T1", snapshotFull: snapshot });
|
const p1 = createPage({ targetId: "T1", snapshotFull: snapshot });
|
||||||
const browser = createBrowser([p1.page]);
|
const browser = createBrowser([p1.page]);
|
||||||
|
|
||||||
(chromium.connectOverCDP as unknown as ReturnType<typeof vi.fn>).mockResolvedValue(browser);
|
(chromiumMock.connectOverCDP as unknown as ReturnType<typeof vi.fn>).mockResolvedValue(browser);
|
||||||
|
|
||||||
const mod = await importModule();
|
|
||||||
const res = await mod.snapshotAiViaPlaywright({
|
const res = await mod.snapshotAiViaPlaywright({
|
||||||
cdpUrl: "http://127.0.0.1:18792",
|
cdpUrl: "http://127.0.0.1:18792",
|
||||||
targetId: "T1",
|
targetId: "T1",
|
||||||
@@ -114,14 +114,12 @@ describe("pw-ai", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("truncates oversized snapshots", async () => {
|
it("truncates oversized snapshots", async () => {
|
||||||
const { chromium } = await import("playwright-core");
|
|
||||||
const longSnapshot = "A".repeat(20);
|
const longSnapshot = "A".repeat(20);
|
||||||
const p1 = createPage({ targetId: "T1", snapshotFull: longSnapshot });
|
const p1 = createPage({ targetId: "T1", snapshotFull: longSnapshot });
|
||||||
const browser = createBrowser([p1.page]);
|
const browser = createBrowser([p1.page]);
|
||||||
|
|
||||||
(chromium.connectOverCDP as unknown as ReturnType<typeof vi.fn>).mockResolvedValue(browser);
|
(chromiumMock.connectOverCDP as unknown as ReturnType<typeof vi.fn>).mockResolvedValue(browser);
|
||||||
|
|
||||||
const mod = await importModule();
|
|
||||||
const res = await mod.snapshotAiViaPlaywright({
|
const res = await mod.snapshotAiViaPlaywright({
|
||||||
cdpUrl: "http://127.0.0.1:18792",
|
cdpUrl: "http://127.0.0.1:18792",
|
||||||
targetId: "T1",
|
targetId: "T1",
|
||||||
@@ -134,12 +132,10 @@ describe("pw-ai", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("clicks a ref using aria-ref locator", async () => {
|
it("clicks a ref using aria-ref locator", async () => {
|
||||||
const { chromium } = await import("playwright-core");
|
|
||||||
const p1 = createPage({ targetId: "T1" });
|
const p1 = createPage({ targetId: "T1" });
|
||||||
const browser = createBrowser([p1.page]);
|
const browser = createBrowser([p1.page]);
|
||||||
(chromium.connectOverCDP as unknown as ReturnType<typeof vi.fn>).mockResolvedValue(browser);
|
(chromiumMock.connectOverCDP as unknown as ReturnType<typeof vi.fn>).mockResolvedValue(browser);
|
||||||
|
|
||||||
const mod = await importModule();
|
|
||||||
await mod.clickViaPlaywright({
|
await mod.clickViaPlaywright({
|
||||||
cdpUrl: "http://127.0.0.1:18792",
|
cdpUrl: "http://127.0.0.1:18792",
|
||||||
targetId: "T1",
|
targetId: "T1",
|
||||||
@@ -151,12 +147,10 @@ describe("pw-ai", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("fails with a clear error when _snapshotForAI is missing", async () => {
|
it("fails with a clear error when _snapshotForAI is missing", async () => {
|
||||||
const { chromium } = await import("playwright-core");
|
|
||||||
const p1 = createPage({ targetId: "T1", hasSnapshotForAI: false });
|
const p1 = createPage({ targetId: "T1", hasSnapshotForAI: false });
|
||||||
const browser = createBrowser([p1.page]);
|
const browser = createBrowser([p1.page]);
|
||||||
(chromium.connectOverCDP as unknown as ReturnType<typeof vi.fn>).mockResolvedValue(browser);
|
(chromiumMock.connectOverCDP as unknown as ReturnType<typeof vi.fn>).mockResolvedValue(browser);
|
||||||
|
|
||||||
const mod = await importModule();
|
|
||||||
await expect(
|
await expect(
|
||||||
mod.snapshotAiViaPlaywright({
|
mod.snapshotAiViaPlaywright({
|
||||||
cdpUrl: "http://127.0.0.1:18792",
|
cdpUrl: "http://127.0.0.1:18792",
|
||||||
@@ -166,13 +160,11 @@ describe("pw-ai", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("reuses the CDP connection for repeated calls", async () => {
|
it("reuses the CDP connection for repeated calls", async () => {
|
||||||
const { chromium } = await import("playwright-core");
|
|
||||||
const p1 = createPage({ targetId: "T1", snapshotFull: "ONE" });
|
const p1 = createPage({ targetId: "T1", snapshotFull: "ONE" });
|
||||||
const browser = createBrowser([p1.page]);
|
const browser = createBrowser([p1.page]);
|
||||||
const connect = vi.spyOn(chromium, "connectOverCDP");
|
const connect = vi.spyOn(chromiumMock, "connectOverCDP");
|
||||||
connect.mockResolvedValue(browser);
|
connect.mockResolvedValue(browser);
|
||||||
|
|
||||||
const mod = await importModule();
|
|
||||||
await mod.snapshotAiViaPlaywright({
|
await mod.snapshotAiViaPlaywright({
|
||||||
cdpUrl: "http://127.0.0.1:18792",
|
cdpUrl: "http://127.0.0.1:18792",
|
||||||
targetId: "T1",
|
targetId: "T1",
|
||||||
|
|||||||
@@ -236,110 +236,6 @@ describe("memory index", () => {
|
|||||||
expect(results[0]?.path).toContain("memory/2026-01-12.md");
|
expect(results[0]?.path).toContain("memory/2026-01-12.md");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("hybrid weights shift ranking between vector and keyword matches", async () => {
|
|
||||||
const manyAlpha = Array.from({ length: 50 }, () => "Alpha").join(" ");
|
|
||||||
await fs.writeFile(
|
|
||||||
path.join(workspaceDir, "memory", "vector-only.md"),
|
|
||||||
"Alpha beta. Alpha beta. Alpha beta. Alpha beta.",
|
|
||||||
);
|
|
||||||
await fs.writeFile(
|
|
||||||
path.join(workspaceDir, "memory", "keyword-only.md"),
|
|
||||||
`${manyAlpha} beta id123.`,
|
|
||||||
);
|
|
||||||
|
|
||||||
const vectorWeightedCfg = {
|
|
||||||
agents: {
|
|
||||||
defaults: {
|
|
||||||
workspace: workspaceDir,
|
|
||||||
memorySearch: {
|
|
||||||
provider: "openai",
|
|
||||||
model: "mock-embed",
|
|
||||||
store: { path: indexPath, vector: { enabled: false } },
|
|
||||||
sync: { watch: false, onSessionStart: false, onSearch: true },
|
|
||||||
query: {
|
|
||||||
minScore: 0,
|
|
||||||
maxResults: 200,
|
|
||||||
hybrid: {
|
|
||||||
enabled: true,
|
|
||||||
vectorWeight: 0.99,
|
|
||||||
textWeight: 0.01,
|
|
||||||
candidateMultiplier: 10,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
list: [{ id: "main", default: true }],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const vectorWeighted = await getMemorySearchManager({
|
|
||||||
cfg: vectorWeightedCfg,
|
|
||||||
agentId: "main",
|
|
||||||
});
|
|
||||||
expect(vectorWeighted.manager).not.toBeNull();
|
|
||||||
if (!vectorWeighted.manager) {
|
|
||||||
throw new Error("manager missing");
|
|
||||||
}
|
|
||||||
manager = vectorWeighted.manager;
|
|
||||||
|
|
||||||
const status = manager.status();
|
|
||||||
if (!status.fts?.available) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await manager.sync({ force: true });
|
|
||||||
const vectorResults = await manager.search("alpha beta id123");
|
|
||||||
expect(vectorResults.length).toBeGreaterThan(0);
|
|
||||||
const vectorPaths = vectorResults.map((r) => r.path);
|
|
||||||
expect(vectorPaths).toContain("memory/vector-only.md");
|
|
||||||
expect(vectorPaths).toContain("memory/keyword-only.md");
|
|
||||||
const vectorOnly = vectorResults.find((r) => r.path === "memory/vector-only.md");
|
|
||||||
const keywordOnly = vectorResults.find((r) => r.path === "memory/keyword-only.md");
|
|
||||||
expect((vectorOnly?.score ?? 0) > (keywordOnly?.score ?? 0)).toBe(true);
|
|
||||||
|
|
||||||
await manager.close();
|
|
||||||
manager = null;
|
|
||||||
|
|
||||||
const textWeightedCfg = {
|
|
||||||
agents: {
|
|
||||||
defaults: {
|
|
||||||
workspace: workspaceDir,
|
|
||||||
memorySearch: {
|
|
||||||
provider: "openai",
|
|
||||||
model: "mock-embed",
|
|
||||||
store: { path: indexPath, vector: { enabled: false } },
|
|
||||||
sync: { watch: false, onSessionStart: false, onSearch: false },
|
|
||||||
query: {
|
|
||||||
minScore: 0,
|
|
||||||
maxResults: 200,
|
|
||||||
hybrid: {
|
|
||||||
enabled: true,
|
|
||||||
vectorWeight: 0.01,
|
|
||||||
textWeight: 0.99,
|
|
||||||
candidateMultiplier: 10,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
list: [{ id: "main", default: true }],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const textWeighted = await getMemorySearchManager({ cfg: textWeightedCfg, agentId: "main" });
|
|
||||||
expect(textWeighted.manager).not.toBeNull();
|
|
||||||
if (!textWeighted.manager) {
|
|
||||||
throw new Error("manager missing");
|
|
||||||
}
|
|
||||||
manager = textWeighted.manager;
|
|
||||||
const keywordResults = await manager.search("alpha beta id123");
|
|
||||||
expect(keywordResults.length).toBeGreaterThan(0);
|
|
||||||
const keywordPaths = keywordResults.map((r) => r.path);
|
|
||||||
expect(keywordPaths).toContain("memory/vector-only.md");
|
|
||||||
expect(keywordPaths).toContain("memory/keyword-only.md");
|
|
||||||
const vectorOnlyAfter = keywordResults.find((r) => r.path === "memory/vector-only.md");
|
|
||||||
const keywordOnlyAfter = keywordResults.find((r) => r.path === "memory/keyword-only.md");
|
|
||||||
expect((keywordOnlyAfter?.score ?? 0) > (vectorOnlyAfter?.score ?? 0)).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("reports vector availability after probe", async () => {
|
it("reports vector availability after probe", async () => {
|
||||||
const cfg = {
|
const cfg = {
|
||||||
agents: {
|
agents: {
|
||||||
|
|||||||
+45
-29
@@ -1,38 +1,54 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
import { markdownTheme } from "./theme.js";
|
|
||||||
|
const cliHighlightMocks = vi.hoisted(() => ({
|
||||||
|
highlight: vi.fn((code: string) => code),
|
||||||
|
supportsLanguage: vi.fn((_lang: string) => true),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("cli-highlight", () => cliHighlightMocks);
|
||||||
|
|
||||||
|
const { markdownTheme } = await import("./theme.js");
|
||||||
|
|
||||||
|
const stripAnsi = (str: string) =>
|
||||||
|
str.replace(new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g"), "");
|
||||||
|
|
||||||
describe("markdownTheme", () => {
|
describe("markdownTheme", () => {
|
||||||
describe("highlightCode", () => {
|
describe("highlightCode", () => {
|
||||||
it("returns highlighted lines for common language inputs", () => {
|
beforeEach(() => {
|
||||||
const code = `const x = 42;`;
|
cliHighlightMocks.highlight.mockReset();
|
||||||
const js = markdownTheme.highlightCode!(code, "javascript");
|
cliHighlightMocks.supportsLanguage.mockReset();
|
||||||
|
cliHighlightMocks.highlight.mockImplementation((code: string) => code);
|
||||||
expect(js).toBeInstanceOf(Array);
|
cliHighlightMocks.supportsLanguage.mockReturnValue(true);
|
||||||
expect(js).toHaveLength(1);
|
|
||||||
expect(js[0]).toContain("const");
|
|
||||||
expect(js[0]).toContain("42");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("handles unknown or missing language and preserves content", () => {
|
it("passes supported language through to the highlighter", () => {
|
||||||
const code = `echo "hello"`;
|
markdownTheme.highlightCode!("const x = 42;", "javascript");
|
||||||
const unknown = markdownTheme.highlightCode!(code, "not-a-real-language");
|
expect(cliHighlightMocks.supportsLanguage).toHaveBeenCalledWith("javascript");
|
||||||
const missing = markdownTheme.highlightCode!(code, undefined);
|
expect(cliHighlightMocks.highlight).toHaveBeenCalledWith(
|
||||||
expect(unknown).toBeInstanceOf(Array);
|
"const x = 42;",
|
||||||
expect(missing).toBeInstanceOf(Array);
|
expect.objectContaining({ language: "javascript" }),
|
||||||
expect(unknown).toHaveLength(1);
|
);
|
||||||
expect(missing).toHaveLength(1);
|
});
|
||||||
expect(unknown[0]).toContain("echo");
|
|
||||||
expect(missing[0]).toContain("echo");
|
|
||||||
const codeBlock = `const message = "Hello, World!";
|
|
||||||
console.log(message);`;
|
|
||||||
const result = markdownTheme.highlightCode!(codeBlock, "javascript");
|
|
||||||
const empty = markdownTheme.highlightCode!("", "javascript");
|
|
||||||
|
|
||||||
const stripAnsi = (str: string) =>
|
it("falls back to auto-detect for unknown language and preserves lines", () => {
|
||||||
str.replace(new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g"), "");
|
cliHighlightMocks.supportsLanguage.mockReturnValue(false);
|
||||||
expect(stripAnsi(result[0])).toBe(`const message = "Hello, World!";`);
|
cliHighlightMocks.highlight.mockImplementation((code: string) => `${code}\nline-2`);
|
||||||
expect(stripAnsi(result[1])).toBe("console.log(message);");
|
const result = markdownTheme.highlightCode!(`echo "hello"`, "not-a-real-language");
|
||||||
expect(empty).toEqual([""]);
|
expect(cliHighlightMocks.highlight).toHaveBeenCalledWith(
|
||||||
|
`echo "hello"`,
|
||||||
|
expect.objectContaining({ language: undefined }),
|
||||||
|
);
|
||||||
|
expect(stripAnsi(result[0] ?? "")).toContain("echo");
|
||||||
|
expect(stripAnsi(result[1] ?? "")).toBe("line-2");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns plain highlighted lines when highlighting throws", () => {
|
||||||
|
cliHighlightMocks.highlight.mockImplementation(() => {
|
||||||
|
throw new Error("boom");
|
||||||
|
});
|
||||||
|
const result = markdownTheme.highlightCode!("echo hello", "javascript");
|
||||||
|
expect(result).toHaveLength(1);
|
||||||
|
expect(stripAnsi(result[0] ?? "")).toBe("echo hello");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+17
-20
@@ -10,9 +10,14 @@ import { loadWebMedia, loadWebMediaRaw, optimizeImageToJpeg } from "./media.js";
|
|||||||
let fixtureRoot = "";
|
let fixtureRoot = "";
|
||||||
let fixtureFileCount = 0;
|
let fixtureFileCount = 0;
|
||||||
let largeJpegBuffer: Buffer;
|
let largeJpegBuffer: Buffer;
|
||||||
|
let largeJpegFile = "";
|
||||||
let tinyPngBuffer: Buffer;
|
let tinyPngBuffer: Buffer;
|
||||||
|
let tinyPngFile = "";
|
||||||
|
let tinyPngWrongExtFile = "";
|
||||||
let alphaPngBuffer: Buffer;
|
let alphaPngBuffer: Buffer;
|
||||||
|
let alphaPngFile = "";
|
||||||
let fallbackPngBuffer: Buffer;
|
let fallbackPngBuffer: Buffer;
|
||||||
|
let fallbackPngFile = "";
|
||||||
let fallbackPngCap = 0;
|
let fallbackPngCap = 0;
|
||||||
|
|
||||||
async function writeTempFile(buffer: Buffer, ext: string): Promise<string> {
|
async function writeTempFile(buffer: Buffer, ext: string): Promise<string> {
|
||||||
@@ -32,8 +37,7 @@ function buildDeterministicBytes(length: number): Buffer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function createLargeTestJpeg(): Promise<{ buffer: Buffer; file: string }> {
|
async function createLargeTestJpeg(): Promise<{ buffer: Buffer; file: string }> {
|
||||||
const file = await writeTempFile(largeJpegBuffer, ".jpg");
|
return { buffer: largeJpegBuffer, file: largeJpegFile };
|
||||||
return { buffer: largeJpegBuffer, file };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
@@ -48,11 +52,14 @@ beforeAll(async () => {
|
|||||||
})
|
})
|
||||||
.jpeg({ quality: 95 })
|
.jpeg({ quality: 95 })
|
||||||
.toBuffer();
|
.toBuffer();
|
||||||
|
largeJpegFile = await writeTempFile(largeJpegBuffer, ".jpg");
|
||||||
tinyPngBuffer = await sharp({
|
tinyPngBuffer = await sharp({
|
||||||
create: { width: 10, height: 10, channels: 3, background: "#00ff00" },
|
create: { width: 10, height: 10, channels: 3, background: "#00ff00" },
|
||||||
})
|
})
|
||||||
.png()
|
.png()
|
||||||
.toBuffer();
|
.toBuffer();
|
||||||
|
tinyPngFile = await writeTempFile(tinyPngBuffer, ".png");
|
||||||
|
tinyPngWrongExtFile = await writeTempFile(tinyPngBuffer, ".bin");
|
||||||
alphaPngBuffer = await sharp({
|
alphaPngBuffer = await sharp({
|
||||||
create: {
|
create: {
|
||||||
width: 64,
|
width: 64,
|
||||||
@@ -63,11 +70,13 @@ beforeAll(async () => {
|
|||||||
})
|
})
|
||||||
.png()
|
.png()
|
||||||
.toBuffer();
|
.toBuffer();
|
||||||
|
alphaPngFile = await writeTempFile(alphaPngBuffer, ".png");
|
||||||
const size = 72;
|
const size = 72;
|
||||||
const raw = buildDeterministicBytes(size * size * 4);
|
const raw = buildDeterministicBytes(size * size * 4);
|
||||||
fallbackPngBuffer = await sharp(raw, { raw: { width: size, height: size, channels: 4 } })
|
fallbackPngBuffer = await sharp(raw, { raw: { width: size, height: size, channels: 4 } })
|
||||||
.png()
|
.png()
|
||||||
.toBuffer();
|
.toBuffer();
|
||||||
|
fallbackPngFile = await writeTempFile(fallbackPngBuffer, ".png");
|
||||||
const smallestPng = await optimizeImageToPng(fallbackPngBuffer, 1);
|
const smallestPng = await optimizeImageToPng(fallbackPngBuffer, 1);
|
||||||
fallbackPngCap = Math.max(1, smallestPng.optimizedSize - 1);
|
fallbackPngCap = Math.max(1, smallestPng.optimizedSize - 1);
|
||||||
const jpegOptimized = await optimizeImageToJpeg(fallbackPngBuffer, fallbackPngCap);
|
const jpegOptimized = await optimizeImageToJpeg(fallbackPngBuffer, fallbackPngCap);
|
||||||
@@ -130,9 +139,7 @@ describe("web media loading", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("sniffs mime before extension when loading local files", async () => {
|
it("sniffs mime before extension when loading local files", async () => {
|
||||||
const wrongExt = await writeTempFile(tinyPngBuffer, ".bin");
|
const result = await loadWebMedia(tinyPngWrongExtFile, 1024 * 1024);
|
||||||
|
|
||||||
const result = await loadWebMedia(wrongExt, 1024 * 1024);
|
|
||||||
|
|
||||||
expect(result.kind).toBe("image");
|
expect(result.kind).toBe("image");
|
||||||
expect(result.contentType).toBe("image/jpeg");
|
expect(result.contentType).toBe("image/jpeg");
|
||||||
@@ -224,9 +231,7 @@ describe("web media loading", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("preserves PNG alpha when under the cap", async () => {
|
it("preserves PNG alpha when under the cap", async () => {
|
||||||
const file = await writeTempFile(alphaPngBuffer, ".png");
|
const result = await loadWebMedia(alphaPngFile, 1024 * 1024);
|
||||||
|
|
||||||
const result = await loadWebMedia(file, 1024 * 1024);
|
|
||||||
|
|
||||||
expect(result.kind).toBe("image");
|
expect(result.kind).toBe("image");
|
||||||
expect(result.contentType).toBe("image/png");
|
expect(result.contentType).toBe("image/png");
|
||||||
@@ -235,9 +240,7 @@ describe("web media loading", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("falls back to JPEG when PNG alpha cannot fit under cap", async () => {
|
it("falls back to JPEG when PNG alpha cannot fit under cap", async () => {
|
||||||
const file = await writeTempFile(fallbackPngBuffer, ".png");
|
const result = await loadWebMedia(fallbackPngFile, fallbackPngCap);
|
||||||
|
|
||||||
const result = await loadWebMedia(file, fallbackPngCap);
|
|
||||||
|
|
||||||
expect(result.kind).toBe("image");
|
expect(result.kind).toBe("image");
|
||||||
expect(result.contentType).toBe("image/jpeg");
|
expect(result.contentType).toBe("image/jpeg");
|
||||||
@@ -247,25 +250,19 @@ describe("web media loading", () => {
|
|||||||
|
|
||||||
describe("local media root guard", () => {
|
describe("local media root guard", () => {
|
||||||
it("rejects local paths outside allowed roots", async () => {
|
it("rejects local paths outside allowed roots", async () => {
|
||||||
const file = await writeTempFile(tinyPngBuffer, ".png");
|
|
||||||
|
|
||||||
// Explicit roots that don't contain the temp file.
|
// Explicit roots that don't contain the temp file.
|
||||||
await expect(
|
await expect(
|
||||||
loadWebMedia(file, 1024 * 1024, { localRoots: ["/nonexistent-root"] }),
|
loadWebMedia(tinyPngFile, 1024 * 1024, { localRoots: ["/nonexistent-root"] }),
|
||||||
).rejects.toThrow(/not under an allowed directory/i);
|
).rejects.toThrow(/not under an allowed directory/i);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("allows local paths under an explicit root", async () => {
|
it("allows local paths under an explicit root", async () => {
|
||||||
const file = await writeTempFile(tinyPngBuffer, ".png");
|
const result = await loadWebMedia(tinyPngFile, 1024 * 1024, { localRoots: [os.tmpdir()] });
|
||||||
|
|
||||||
const result = await loadWebMedia(file, 1024 * 1024, { localRoots: [os.tmpdir()] });
|
|
||||||
expect(result.kind).toBe("image");
|
expect(result.kind).toBe("image");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("allows any path when localRoots is 'any'", async () => {
|
it("allows any path when localRoots is 'any'", async () => {
|
||||||
const file = await writeTempFile(tinyPngBuffer, ".png");
|
const result = await loadWebMedia(tinyPngFile, 1024 * 1024, { localRoots: "any" });
|
||||||
|
|
||||||
const result = await loadWebMedia(file, 1024 * 1024, { localRoots: "any" });
|
|
||||||
expect(result.kind).toBe("image");
|
expect(result.kind).toBe("image");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user