From faf4e68f9339fc24972cdf9c8a706c58289332ca Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 15 Sep 2026 05:48:36 -0700 Subject: [PATCH] refactor(tests): reuse Google Chat runtime spies (#148699) --- .../googlechat/src/monitor-ingress.test.ts | 3 +- .../src/monitor.reply-delivery.test.ts | 28 +++++++---------- extensions/googlechat/src/monitor.test.ts | 31 ++++++++++--------- 3 files changed, 29 insertions(+), 33 deletions(-) diff --git a/extensions/googlechat/src/monitor-ingress.test.ts b/extensions/googlechat/src/monitor-ingress.test.ts index 4d4e57170904..67d0852ade5d 100644 --- a/extensions/googlechat/src/monitor-ingress.test.ts +++ b/extensions/googlechat/src/monitor-ingress.test.ts @@ -7,6 +7,7 @@ import { createChannelIngressQueueForTests, } from "openclaw/plugin-sdk/channel-ingress-test-runtime"; import { afterEach, describe, expect, it, vi } from "vitest"; +import { createRuntimeSpies } from "../../test-support/runtime-spies.js"; import { createGoogleChatIngressMonitor } from "./monitor-ingress.js"; type GoogleChatIngressQueue = NonNullable< @@ -64,7 +65,7 @@ function startIngress(queue: GoogleChatIngressQueue, dispatch: GoogleChatIngress accountId: "default", queue, dispatch, - runtime: { error: vi.fn(), log: vi.fn() }, + runtime: createRuntimeSpies(), pollIntervalMs: 10, adoptionStallTimeoutMs: 5_000, }); diff --git a/extensions/googlechat/src/monitor.reply-delivery.test.ts b/extensions/googlechat/src/monitor.reply-delivery.test.ts index cacd24802956..64a6ee8b2170 100644 --- a/extensions/googlechat/src/monitor.reply-delivery.test.ts +++ b/extensions/googlechat/src/monitor.reply-delivery.test.ts @@ -1,10 +1,11 @@ // Googlechat tests cover monitor.reply delivery plugin behavior. import { PlatformMessageNotDispatchedError } from "openclaw/plugin-sdk/error-runtime"; import { afterAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { createRuntimeSpies } from "../../test-support/runtime-spies.js"; import type { OpenClawConfig } from "../runtime-api.js"; import type { ResolvedGoogleChatAccount } from "./accounts.js"; import { GoogleChatApiError } from "./api.js"; -import type { GoogleChatCoreRuntime, GoogleChatRuntimeEnv } from "./monitor-types.js"; +import type { GoogleChatCoreRuntime } from "./monitor-types.js"; const mocks = vi.hoisted(() => ({ deleteGoogleChatMessage: vi.fn(), @@ -45,13 +46,6 @@ function createCore(params?: { } as unknown as GoogleChatCoreRuntime; } -function createRuntime() { - return { - error: vi.fn(), - log: vi.fn(), - } satisfies GoogleChatRuntimeEnv; -} - let createGoogleChatTypingMessage: typeof import("./monitor-reply-delivery.js").createGoogleChatTypingMessage; let deliverGoogleChatReply: typeof import("./monitor-reply-delivery.js").deliverGoogleChatReply; @@ -71,7 +65,7 @@ afterAll(() => { describe("Google Chat reply delivery", () => { it("does not resend the first chunk when the typing update result is ambiguous", async () => { const core = createCore({ chunks: ["first chunk", "second chunk"] }); - const runtime = createRuntime(); + const runtime = createRuntimeSpies(); const statusSink = vi.fn(); const updateError = new Error("response lost"); mocks.updateGoogleChatMessage.mockRejectedValueOnce(updateError); @@ -113,7 +107,7 @@ describe("Google Chat reply delivery", () => { payload: { text: "two chunks", replyToId: "spaces/AAA/threads/root" }, account, spaceId: "spaces/AAA", - runtime: createRuntime(), + runtime: createRuntimeSpies(), core, config, typingMessage: createGoogleChatTypingMessage({ @@ -131,7 +125,7 @@ describe("Google Chat reply delivery", () => { it("continues later chunks in the provider fallback thread", async () => { const core = createCore({ chunks: ["first chunk", "second chunk"] }); - const runtime = createRuntime(); + const runtime = createRuntimeSpies(); mocks.sendGoogleChatMessage .mockResolvedValueOnce({ messageName: "spaces/AAA/messages/first", @@ -167,7 +161,7 @@ describe("Google Chat reply delivery", () => { it("continues after a fallback typing placeholder in its delivered thread", async () => { const core = createCore({ chunks: ["first chunk", "second chunk"] }); - const runtime = createRuntime(); + const runtime = createRuntimeSpies(); mocks.sendGoogleChatMessage.mockResolvedValueOnce({ messageName: "spaces/AAA/messages/second", threadName: "spaces/AAA/threads/fallback", @@ -203,7 +197,7 @@ describe("Google Chat reply delivery", () => { it("keeps the requested thread when the provider omits thread metadata", async () => { const core = createCore({ chunks: ["first chunk", "second chunk"] }); - const runtime = createRuntime(); + const runtime = createRuntimeSpies(); mocks.sendGoogleChatMessage.mockResolvedValue({ messageName: "spaces/AAA/messages/sent", }); @@ -225,7 +219,7 @@ describe("Google Chat reply delivery", () => { it("keeps top-level chunks top-level when Google returns a thread name", async () => { const core = createCore({ chunks: ["first chunk", "second chunk"] }); - const runtime = createRuntime(); + const runtime = createRuntimeSpies(); mocks.sendGoogleChatMessage.mockResolvedValue({ messageName: "spaces/AAA/messages/sent", threadName: "spaces/AAA/threads/provider-created", @@ -248,7 +242,7 @@ describe("Google Chat reply delivery", () => { it("replaces a typing message when the final reply target changed", async () => { const core = createCore(); - const runtime = createRuntime(); + const runtime = createRuntimeSpies(); mocks.sendGoogleChatMessage.mockResolvedValue({ messageName: "spaces/AAA/messages/reply" }); await deliverGoogleChatReply({ @@ -283,7 +277,7 @@ describe("Google Chat reply delivery", () => { const core = createCore({ media: { buffer: Buffer.from("image"), contentType: "image/png", fileName: "reply.png" }, }); - const runtime = createRuntime(); + const runtime = createRuntimeSpies(); await deliverGoogleChatReply({ payload: { @@ -319,7 +313,7 @@ describe("Google Chat reply delivery", () => { it("cleans up typing and rejects media-only replies without provider upload access", async () => { const core = createCore(); - const runtime = createRuntime(); + const runtime = createRuntimeSpies(); await expect( deliverGoogleChatReply({ diff --git a/extensions/googlechat/src/monitor.test.ts b/extensions/googlechat/src/monitor.test.ts index 126df68d0c52..4c4159ea42fb 100644 --- a/extensions/googlechat/src/monitor.test.ts +++ b/extensions/googlechat/src/monitor.test.ts @@ -9,6 +9,7 @@ import { } from "openclaw/plugin-sdk/channel-ingress-test-runtime"; import { MediaFetchError } from "openclaw/plugin-sdk/media-runtime"; import { beforeEach, describe, expect, it, vi } from "vitest"; +import { createRuntimeSpies } from "../../test-support/runtime-spies.js"; import type { ResolvedGoogleChatAccount } from "./accounts.js"; import { createGoogleChatIngressMonitor, @@ -201,7 +202,7 @@ describe("googlechat monitor bot loop protection", () => { inbound: { run: runTurn }, }, } as unknown as GoogleChatCoreRuntime; - const runtime = { error: vi.fn(), log: vi.fn() } satisfies GoogleChatRuntimeEnv; + const runtime = createRuntimeSpies() satisfies GoogleChatRuntimeEnv; const account = { accountId, config: { @@ -298,7 +299,7 @@ describe("googlechat monitor inbound space classification", () => { event, account, config: {}, - runtime: { error: vi.fn(), log: vi.fn() }, + runtime: createRuntimeSpies(), core, mediaMaxMb: 10, }); @@ -351,7 +352,7 @@ describe("googlechat monitor inbound space classification", () => { }), account: googleChatMediaTestAccount, config: {}, - runtime: { error: vi.fn(), log: vi.fn() }, + runtime: createRuntimeSpies(), core, mediaMaxMb: 10, }); @@ -414,7 +415,7 @@ describe("googlechat monitor inbound space classification", () => { }, ])("explains an unsupported $name without downloading it", async ({ text, driveDataRef }) => { const { buildContext, core, runTurn, saveMediaBuffer } = createInboundClassificationHarness(); - const runtime = { error: vi.fn(), log: vi.fn() }; + const runtime = createRuntimeSpies(); allowGoogleChatMediaSender(); await processGoogleChatTestEvent({ @@ -458,7 +459,7 @@ describe("googlechat monitor inbound space classification", () => { { name: "no caption", text: "" }, ])("keeps $name and every attachment fact when the first file is oversized", async ({ text }) => { const { buildContext, core, runTurn, saveMediaBuffer } = createInboundClassificationHarness(); - const runtime = { error: vi.fn(), log: vi.fn() }; + const runtime = createRuntimeSpies(); const turnAdoptionLifecycle = { admission: "exclusive", onAdopted: vi.fn(async () => {}), @@ -560,7 +561,7 @@ describe("googlechat monitor inbound space classification", () => { }), account: googleChatMediaTestAccount, config: {}, - runtime: { error: vi.fn(), log: vi.fn() }, + runtime: createRuntimeSpies(), core, mediaMaxMb: 10, }), @@ -577,7 +578,7 @@ describe("googlechat monitor inbound space classification", () => { stateDir, }); const { buildContext, core, runTurn } = createInboundClassificationHarness(); - const runtime = { error: vi.fn(), log: vi.fn() }; + const runtime = createRuntimeSpies(); const account = googleChatMediaTestAccount; const receivedBodies: string[] = []; apiMocks.downloadGoogleChatMedia.mockRejectedValueOnce( @@ -673,7 +674,7 @@ describe("googlechat monitor inbound space classification", () => { credentialSource: "inline", } as ResolvedGoogleChatAccount, config: {}, - runtime: { error: vi.fn(), log: vi.fn() }, + runtime: createRuntimeSpies(), core, mediaMaxMb: 10, turnAdoptionLifecycle, @@ -720,7 +721,7 @@ describe("googlechat monitor inbound space classification", () => { event, account, config: {}, - runtime: { error: vi.fn(), log: vi.fn() }, + runtime: createRuntimeSpies(), core, mediaMaxMb: 10, }); @@ -787,7 +788,7 @@ describe("googlechat monitor inbound space classification", () => { event, account, config: { agents: { entries: { "agent-1": agent } } }, - runtime: { error: vi.fn(), log: vi.fn() }, + runtime: createRuntimeSpies(), core, mediaMaxMb: 10, }); @@ -870,7 +871,7 @@ describe("googlechat monitor inbound space classification", () => { event, account, config: {}, - runtime: { error: vi.fn(), log: vi.fn() }, + runtime: createRuntimeSpies(), core, mediaMaxMb: 10, }); @@ -927,7 +928,7 @@ describe("googlechat monitor sender bot status", () => { credentialSource: "inline", } as ResolvedGoogleChatAccount, config: {}, - runtime: { error: vi.fn(), log: vi.fn() }, + runtime: createRuntimeSpies(), core, mediaMaxMb: 10, }); @@ -955,7 +956,7 @@ describe("googlechat monitor sender bot status", () => { credentialSource: "inline", } as ResolvedGoogleChatAccount, config: {}, - runtime: { error: vi.fn(), log: vi.fn() }, + runtime: createRuntimeSpies(), core, mediaMaxMb: 10, }); @@ -969,7 +970,7 @@ describe("googlechat monitor sender bot status", () => { describe("googlechat monitor direct messages", () => { it("omits thread metadata from DM reply context and typing messages", async () => { const { buildContext, core, runTurn } = createInboundClassificationHarness(); - const runtime = { error: vi.fn(), log: vi.fn() } satisfies GoogleChatRuntimeEnv; + const runtime = createRuntimeSpies() satisfies GoogleChatRuntimeEnv; const account = { accountId: "work", config: { @@ -1024,7 +1025,7 @@ describe("googlechat monitor direct messages", () => { it("drops invalid event timestamps from inbound runtime payloads", async () => { const { buildContext, core, runTurn } = createInboundClassificationHarness(); - const runtime = { error: vi.fn(), log: vi.fn() } satisfies GoogleChatRuntimeEnv; + const runtime = createRuntimeSpies() satisfies GoogleChatRuntimeEnv; const account = { accountId: "work", config: {