From b433e643bb231001231f0018e85c1880a26df12d Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 11 May 2026 23:36:06 +0100 Subject: [PATCH] test: guard media understanding mock calls --- src/media-understanding/image.test.ts | 50 +++++++++++++++---- .../media-understanding-misc.test.ts | 10 +++- .../runner.cli-audio.test.ts | 10 +++- src/media-understanding/shared.test.ts | 10 ++-- 4 files changed, 66 insertions(+), 14 deletions(-) diff --git a/src/media-understanding/image.test.ts b/src/media-understanding/image.test.ts index 6ca1f267308c..8d8bf1193ee1 100644 --- a/src/media-understanding/image.test.ts +++ b/src/media-understanding/image.test.ts @@ -48,6 +48,21 @@ type AuthRequestCall = { store?: unknown; }; +function requireFirstMockCall(mock: { mock: { calls: unknown[][] } }, label: string): unknown[] { + const [call] = mock.mock.calls; + if (!call) { + throw new Error(`Expected ${label} call`); + } + return call; +} + +function requireRecord(value: unknown, label: string): Record { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`Expected ${label}`); + } + return value as Record; +} + vi.mock("@earendil-works/pi-ai", async () => { const actual = await vi.importActual("@earendil-works/pi-ai"); @@ -180,7 +195,8 @@ describe("describeImageWithModel", () => { expect(authRequest?.store).toBe(authStore); expect(requireApiKeyMock).toHaveBeenCalled(); expect(setRuntimeApiKeyMock).toHaveBeenCalledWith("minimax-portal", "oauth-test"); - const [fetchUrl, fetchOptions] = fetchMock.mock.calls[0] ?? []; + const [fetchUrl, fetchOptionsValue] = requireFirstMockCall(fetchMock, "fetch"); + const fetchOptions = requireRecord(fetchOptionsValue, "fetch options"); expect(fetchUrl).toBe("https://api.minimax.io/v1/coding_plan/vlm"); expect(fetchOptions).toEqual({ method: "POST", @@ -193,9 +209,9 @@ describe("describeImageWithModel", () => { prompt: "Describe the image.", image_url: `data:image/png;base64,${Buffer.from("png-bytes").toString("base64")}`, }), - signal: fetchOptions?.signal, + signal: fetchOptions.signal, }); - expect(fetchOptions?.signal).toBeInstanceOf(AbortSignal); + expect(fetchOptions.signal).toBeInstanceOf(AbortSignal); expect(timeoutSpy).toHaveBeenCalledWith(1000); expect(completeMock).not.toHaveBeenCalled(); }); @@ -235,7 +251,10 @@ describe("describeImageWithModel", () => { text: "generic ok", model: "custom-vision", }); - const [streamRequest] = registerProviderStreamForModelMock.mock.calls[0] ?? []; + const [streamRequest] = requireFirstMockCall( + registerProviderStreamForModelMock, + "provider stream registration", + ); expect(streamRequest).toEqual({ model: { provider: "minimax-portal", @@ -309,11 +328,24 @@ describe("describeImageWithModel", () => { model: "google/gemma-4-e2b", }); expect(registryFind).not.toHaveBeenCalled(); - const [resolveRequest] = resolveModelWithRegistryMock.mock.calls[0] ?? []; - expect(resolveRequest?.provider).toBe("lmstudio"); - expect(resolveRequest?.modelId).toBe("google/gemma-4-e2b"); - expect(resolveRequest?.agentDir).toBe("/tmp/openclaw-agent"); - expect(resolveRequest?.cfg.models?.providers?.lmstudio?.baseUrl).toBe("http://127.0.0.1:1234"); + const [resolveRequestValue] = requireFirstMockCall( + resolveModelWithRegistryMock, + "model registry resolution", + ); + const resolveRequest = requireRecord(resolveRequestValue, "model registry request"); + expect(resolveRequest.provider).toBe("lmstudio"); + expect(resolveRequest.modelId).toBe("google/gemma-4-e2b"); + expect(resolveRequest.agentDir).toBe("/tmp/openclaw-agent"); + expect( + requireRecord( + requireRecord( + requireRecord(requireRecord(resolveRequest.cfg, "request config").models, "models") + .providers, + "model providers", + ).lmstudio, + "lmstudio provider", + ).baseUrl, + ).toBe("http://127.0.0.1:1234"); expect(prepareProviderDynamicModelMock).not.toHaveBeenCalled(); expect(completeMock).toHaveBeenCalledOnce(); }); diff --git a/src/media-understanding/media-understanding-misc.test.ts b/src/media-understanding/media-understanding-misc.test.ts index c483d074004d..d1ac691576ac 100644 --- a/src/media-understanding/media-understanding-misc.test.ts +++ b/src/media-understanding/media-understanding-misc.test.ts @@ -55,6 +55,14 @@ describe("media understanding attachments SSRF", () => { vi.restoreAllMocks(); }); + function requireFirstOpenCall(openSpy: ReturnType): unknown[] { + const [call] = openSpy.mock.calls; + if (!call) { + throw new Error("expected fs.open call"); + } + return call; + } + it("blocks private IP URLs before fetching", async () => { const fetchSpy = vi.fn(); globalThis.fetch = withFetchPreconnect(fetchSpy); @@ -212,7 +220,7 @@ describe("media understanding attachments SSRF", () => { await cache.getBuffer({ attachmentIndex: 0, maxBytes: 1024, timeoutMs: 1000 }); expect(openSpy).toHaveBeenCalled(); - const [openedPath, openedFlags] = openSpy.mock.calls[0] ?? []; + const [openedPath, openedFlags] = requireFirstOpenCall(openSpy); expect(await fs.realpath(String(openedPath)).catch(() => String(openedPath))).toBe( canonicalAttachmentPath, ); diff --git a/src/media-understanding/runner.cli-audio.test.ts b/src/media-understanding/runner.cli-audio.test.ts index 0d48212e6a15..d8e3c1251cff 100644 --- a/src/media-understanding/runner.cli-audio.test.ts +++ b/src/media-understanding/runner.cli-audio.test.ts @@ -12,6 +12,14 @@ vi.mock("../process/exec.js", () => ({ let runCliEntry: typeof import("./runner.entries.js").runCliEntry; +function requireFirstRunExecCall(): unknown[] { + const [call] = runExecMock.mock.calls; + if (!call) { + throw new Error("expected runExec call"); + } + return call; +} + describe("media-understanding CLI audio entry", () => { beforeAll(async () => { ({ runCliEntry } = await import("./runner.entries.js")); @@ -65,7 +73,7 @@ describe("media-understanding CLI audio entry", () => { }); expect(runExecMock).toHaveBeenCalledTimes(1); - const [command, args, options] = runExecMock.mock.calls[0] ?? []; + const [command, args, options] = requireFirstRunExecCall(); expect(command).toBe("mock-transcriber"); expect(args).toEqual(["--prompt", "Focus on names", "--language", "en", "--file", mediaPath]); expect(options).toEqual({ diff --git a/src/media-understanding/shared.test.ts b/src/media-understanding/shared.test.ts index 7a600060471c..a5109d9a65c2 100644 --- a/src/media-understanding/shared.test.ts +++ b/src/media-understanding/shared.test.ts @@ -48,11 +48,15 @@ afterEach(() => { }); function getFirstGuardedFetchCall() { - const [call] = fetchWithSsrFGuardMock.mock.calls[0] ?? []; - if (!call) { + const [mockCall] = fetchWithSsrFGuardMock.mock.calls; + if (!mockCall) { throw new Error("Expected fetchWithSsrFGuard to be called"); } - return call; + const [request] = mockCall; + if (!request || typeof request !== "object" || Array.isArray(request)) { + throw new Error("Expected fetchWithSsrFGuard request"); + } + return request as Record; } describe("provider operation deadlines", () => {