diff --git a/src/auto-reply/reply/commands-stop-target.test.ts b/src/auto-reply/reply/commands-stop-target.test.ts index 6543b6e1cad..3913161268b 100644 --- a/src/auto-reply/reply/commands-stop-target.test.ts +++ b/src/auto-reply/reply/commands-stop-target.test.ts @@ -154,12 +154,23 @@ describe("handleStopCommand target fallback", () => { }); expect(replyRunAbortMock).toHaveBeenCalledWith("agent:target:telegram:direct:123"); expect(abortEmbeddedPiRunMock).not.toHaveBeenCalledWith("wrapper-session-id"); - const persistAbortTargetParams = persistAbortTargetEntryMock.mock.calls[0]?.[0]; + const [[persistAbortTargetParams]] = persistAbortTargetEntryMock.mock.calls as unknown as Array< + [ + { + key?: string; + entry?: unknown; + sessionStore?: unknown; + storePath?: string; + }, + ] + >; expect(persistAbortTargetParams?.key).toBe("agent:target:telegram:direct:123"); expect(persistAbortTargetParams?.entry).toBeUndefined(); expect(persistAbortTargetParams?.sessionStore).toBe(params.sessionStore); expect(persistAbortTargetParams?.storePath).toBe("/tmp/sessions.json"); - const stopSubagentsParams = stopSubagentsForRequesterMock.mock.calls[0]?.[0]; + const [[stopSubagentsParams]] = stopSubagentsForRequesterMock.mock.calls as unknown as Array< + [{ cfg?: unknown; requesterSessionKey?: string }] + >; expect(stopSubagentsParams?.cfg).toBe(params.cfg); expect(stopSubagentsParams?.requesterSessionKey).toBe("agent:target:telegram:direct:123"); expect(createInternalHookEventMock).toHaveBeenCalledWith( diff --git a/src/auto-reply/reply/dispatch-from-config.reply-dispatch.test.ts b/src/auto-reply/reply/dispatch-from-config.reply-dispatch.test.ts index b80d843b132..43405383169 100644 --- a/src/auto-reply/reply/dispatch-from-config.reply-dispatch.test.ts +++ b/src/auto-reply/reply/dispatch-from-config.reply-dispatch.test.ts @@ -118,7 +118,18 @@ describe("dispatchReplyFromConfig reply_dispatch hook", () => { expect(hookMocks.runner.runReplyDispatch).toHaveBeenCalledOnce(); const [replyDispatchEvent, replyDispatchRuntime] = - hookMocks.runner.runReplyDispatch.mock.calls[0] ?? []; + (hookMocks.runner.runReplyDispatch.mock.calls[0] as + | [ + { + sessionKey?: string; + sendPolicy?: string; + inboundAudio?: boolean; + }, + { + cfg?: unknown; + }, + ] + | undefined) ?? []; expect(replyDispatchEvent?.sessionKey).toBe("agent:test:session"); expect(replyDispatchEvent?.sendPolicy).toBe("allow"); expect(replyDispatchEvent?.inboundAudio).toBe(false); diff --git a/src/plugin-sdk/channel-lifecycle.queue.test.ts b/src/plugin-sdk/channel-lifecycle.queue.test.ts index b592d52f97e..4f7a9f25e33 100644 --- a/src/plugin-sdk/channel-lifecycle.queue.test.ts +++ b/src/plugin-sdk/channel-lifecycle.queue.test.ts @@ -68,11 +68,15 @@ describe("createChannelRunQueue", () => { await flushAsyncWork(); - expect(setStatus).toHaveBeenCalledWith({ activeRuns: 0, busy: false }); - expect(setStatus).toHaveBeenCalledWith(expect.objectContaining({ activeRuns: 1, busy: true })); - expect(setStatus).toHaveBeenLastCalledWith( - expect.objectContaining({ activeRuns: 0, busy: false }), - ); + expect(setStatus).toHaveBeenCalledTimes(3); + const [initialStatus, busyStatus, finalStatus] = setStatus.mock.calls.map(([status]) => status); + expect(initialStatus).toEqual({ activeRuns: 0, busy: false }); + expect(busyStatus?.activeRuns).toBe(1); + expect(busyStatus?.busy).toBe(true); + expect(typeof busyStatus?.lastRunActivityAt).toBe("number"); + expect(finalStatus?.activeRuns).toBe(0); + expect(finalStatus?.busy).toBe(false); + expect(typeof finalStatus?.lastRunActivityAt).toBe("number"); expect(onError).toHaveBeenCalledWith(taskError); }); diff --git a/src/plugin-sdk/channel-streaming.test.ts b/src/plugin-sdk/channel-streaming.test.ts index 324f948f40a..0715a746828 100644 --- a/src/plugin-sdk/channel-streaming.test.ts +++ b/src/plugin-sdk/channel-streaming.test.ts @@ -279,20 +279,21 @@ describe("channel-streaming", () => { }); it("formats progress draft lines with shared tool display labels", () => { - expect( - buildChannelProgressDraftLine({ - event: "tool", - name: "write", - args: { path: "/tmp/demo/index.html" }, - }), - ).toMatchObject({ - kind: "tool", - icon: "✍️", - label: "Write", - detail: "to /tmp/demo/index.html", - text: "✍️ Write: to /tmp/demo/index.html", - toolName: "write", + const progressLine = buildChannelProgressDraftLine({ + event: "tool", + name: "write", + args: { path: "/tmp/demo/index.html" }, }); + expect(progressLine).toBeDefined(); + if (!progressLine) { + throw new Error("expected tool progress draft line"); + } + expect(progressLine.kind).toBe("tool"); + expect(progressLine.icon).toBe("✍️"); + expect(progressLine.label).toBe("Write"); + expect(progressLine.detail).toBe("to /tmp/demo/index.html"); + expect(progressLine.text).toBe("✍️ Write: to /tmp/demo/index.html"); + expect(progressLine.toolName).toBe("write"); expect( formatChannelProgressDraftLine({ event: "tool", diff --git a/src/plugin-sdk/delivery-queue-runtime.test.ts b/src/plugin-sdk/delivery-queue-runtime.test.ts index 531bb444fef..9a6d7bf108a 100644 --- a/src/plugin-sdk/delivery-queue-runtime.test.ts +++ b/src/plugin-sdk/delivery-queue-runtime.test.ts @@ -46,11 +46,10 @@ describe("plugin-sdk delivery queue drainPendingDeliveries", () => { selectEntry: () => ({ match: false }), }); - expect(mocks.coreDrainPendingDeliveries).toHaveBeenCalledWith( - expect.objectContaining({ - deliver: mocks.deliverOutboundPayloads, - }), - ); + expect(mocks.coreDrainPendingDeliveries).toHaveBeenCalledTimes(1); + const [[{ deliver: lazyDeliver }]] = mocks.coreDrainPendingDeliveries.mock + .calls as unknown as Array<[{ deliver?: unknown }]>; + expect(lazyDeliver).toBe(mocks.deliverOutboundPayloads); }); it("preserves an explicit deliver fn without loading the lazy runtime", async () => { @@ -65,11 +64,10 @@ describe("plugin-sdk delivery queue drainPendingDeliveries", () => { selectEntry: () => ({ match: false }), }); - expect(mocks.coreDrainPendingDeliveries).toHaveBeenCalledWith( - expect.objectContaining({ - deliver, - }), - ); + expect(mocks.coreDrainPendingDeliveries).toHaveBeenCalledTimes(1); + const [[{ deliver: explicitDeliver }]] = mocks.coreDrainPendingDeliveries.mock + .calls as unknown as Array<[{ deliver?: unknown }]>; + expect(explicitDeliver).toBe(deliver); expect(mocks.deliverOutboundPayloads).not.toHaveBeenCalled(); }); }); diff --git a/src/plugin-sdk/discord.test.ts b/src/plugin-sdk/discord.test.ts index b8fe32e1801..d6bf59ab8f2 100644 --- a/src/plugin-sdk/discord.test.ts +++ b/src/plugin-sdk/discord.test.ts @@ -160,19 +160,17 @@ describe("discord plugin-sdk facade", () => { childSessionKey: "child", }); - expect(mocks.runtimeModule.autoBindSpawnedDiscordSubagent).toHaveBeenCalledWith( - expect.objectContaining({ - agentId: "agent", - cfg: mocks.runtimeConfig, - childSessionKey: "child", - }), - ); - expect(binding).toEqual( - expect.objectContaining({ - cfg: mocks.runtimeConfig, - targetKind: "subagent", - targetSessionKey: "child", - }), - ); + expect(mocks.runtimeModule.autoBindSpawnedDiscordSubagent).toHaveBeenCalledTimes(1); + const callParams = mocks.runtimeModule.autoBindSpawnedDiscordSubagent.mock.calls[0]?.[0]; + expect(callParams.agentId).toBe("agent"); + expect(callParams.cfg).toBe(mocks.runtimeConfig); + expect(callParams.childSessionKey).toBe("child"); + expect(binding).not.toBeNull(); + if (!binding) { + throw new Error("expected Discord subagent binding"); + } + expect(binding.cfg).toBe(mocks.runtimeConfig); + expect(binding.targetKind).toBe("subagent"); + expect(binding.targetSessionKey).toBe("child"); }); }); diff --git a/src/plugin-sdk/file-lock.test.ts b/src/plugin-sdk/file-lock.test.ts index 20b7dab593e..f0ef3a8f50c 100644 --- a/src/plugin-sdk/file-lock.test.ts +++ b/src/plugin-sdk/file-lock.test.ts @@ -44,10 +44,9 @@ describe("acquireFileLock", () => { ); await expect(acquireFileLock(filePath, options)).rejects.toSatisfy((error) => { - expect(error).toMatchObject({ - code: FILE_LOCK_TIMEOUT_ERROR_CODE, - }); - expect((error as { lockPath?: string }).lockPath).toMatch(/oauth-refresh\.lock$/); + const lockError = error as { code?: string; lockPath?: string }; + expect(lockError.code).toBe(FILE_LOCK_TIMEOUT_ERROR_CODE); + expect(lockError.lockPath).toMatch(/oauth-refresh\.lock$/); return true; }); }, 5_000); diff --git a/src/plugin-sdk/provider-tools.test.ts b/src/plugin-sdk/provider-tools.test.ts index 5a166ac324d..341b972a210 100644 --- a/src/plugin-sdk/provider-tools.test.ts +++ b/src/plugin-sdk/provider-tools.test.ts @@ -196,12 +196,12 @@ describe("buildProviderToolCompatFamilyHooks", () => { }); expect(normalized[0]?.parameters).toEqual(permissiveParameters); - expect(findOpenAIStrictSchemaViolations(permissiveParameters, "cron.parameters")).toEqual( - expect.arrayContaining([ - "cron.parameters.required.schedule", - "cron.parameters.additionalProperties", - ]), + const strictSchemaViolations = findOpenAIStrictSchemaViolations( + permissiveParameters, + "cron.parameters", ); + expect(strictSchemaViolations).toContain("cron.parameters.required.schedule"); + expect(strictSchemaViolations).toContain("cron.parameters.additionalProperties"); expect( hooks.inspectToolSchemas({ provider: "openai", diff --git a/src/plugin-sdk/reply-payload.test.ts b/src/plugin-sdk/reply-payload.test.ts index f16fab9f171..9e8d829a3f2 100644 --- a/src/plugin-sdk/reply-payload.test.ts +++ b/src/plugin-sdk/reply-payload.test.ts @@ -518,14 +518,21 @@ describe("sendMediaWithLeadingCaption", () => { }), ).resolves.toBe(true); - expect(onError).toHaveBeenCalledWith( - expect.objectContaining({ - mediaUrl: "https://example.com/a.png", - caption: "hello", - index: 0, - isFirst: true, - }), - ); + expect(onError).toHaveBeenCalledTimes(1); + const [[errorPayload]] = onError.mock.calls as unknown as Array< + [ + { + mediaUrl?: string; + caption?: string; + index?: number; + isFirst?: boolean; + }, + ] + >; + expect(errorPayload.mediaUrl).toBe("https://example.com/a.png"); + expect(errorPayload.caption).toBe("hello"); + expect(errorPayload.index).toBe(0); + expect(errorPayload.isFirst).toBe(true); expect(send).toHaveBeenNthCalledWith(2, { mediaUrl: "https://example.com/b.png", caption: undefined, diff --git a/src/plugin-sdk/telegram-account.test.ts b/src/plugin-sdk/telegram-account.test.ts index 7f7088207ae..458b75b135e 100644 --- a/src/plugin-sdk/telegram-account.test.ts +++ b/src/plugin-sdk/telegram-account.test.ts @@ -36,11 +36,7 @@ describe("telegram account plugin-sdk compatibility facade", () => { cfg, accountId: "default", }); - expect(account).toEqual( - expect.objectContaining({ - accountId: "default", - token: "token", - }), - ); + expect(account.accountId).toBe("default"); + expect(account.token).toBe("token"); }); }); diff --git a/src/plugin-sdk/temp-path.test.ts b/src/plugin-sdk/temp-path.test.ts index 79e111c48cc..a496c2be0bb 100644 --- a/src/plugin-sdk/temp-path.test.ts +++ b/src/plugin-sdk/temp-path.test.ts @@ -86,7 +86,10 @@ describe("withTempDownloadPath", () => { expect(capturedPath).toContain(path.join(resolvePreferredOpenClawTmpDir(), "line-media-")); } if (expectCleanup) { - await expect(fs.stat(capturedPath)).rejects.toMatchObject({ code: "ENOENT" }); + await expect(fs.stat(capturedPath)).rejects.toSatisfy((error) => { + expect((error as NodeJS.ErrnoException).code).toBe("ENOENT"); + return true; + }); } }); }); diff --git a/src/plugin-sdk/webhook-targets.test.ts b/src/plugin-sdk/webhook-targets.test.ts index 4b30e1b3494..202194977d9 100644 --- a/src/plugin-sdk/webhook-targets.test.ts +++ b/src/plugin-sdk/webhook-targets.test.ts @@ -85,7 +85,7 @@ describe("registerWebhookTarget", () => { expect(onFirstPathTarget).toHaveBeenCalledTimes(1); expect(onFirstPathTarget).toHaveBeenCalledWith({ path: "/hook", - target: expect.objectContaining({ id: "A", path: "/hook" }), + target: registeredA.target, }); registeredB.unregister(); @@ -147,13 +147,13 @@ describe("registerWebhookTargetWithPluginRoute", () => { }); expect(registry.httpRoutes).toHaveLength(1); - expect(registry.httpRoutes[0]).toEqual( - expect.objectContaining({ - pluginId: "demo", - path: "/hook", - source: "demo-webhook", - }), - ); + const route = registry.httpRoutes[0]; + if (!route) { + throw new Error("expected plugin route to be registered"); + } + expect(route.pluginId).toBe("demo"); + expect(route.path).toBe("/hook"); + expect(route.source).toBe("demo-webhook"); registeredA.unregister(); expect(registry.httpRoutes).toHaveLength(1);