From d3b15c6afa16661562e5a9201822e157683f9131 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 18 Jan 2026 06:58:46 +0000 Subject: [PATCH] ci: stabilize vitest runs --- ...onfig.should-use-global-sandbox-config-no-agent.test.ts | 2 +- src/agents/sandbox-merge.test.ts | 2 +- ...ing.stages-inbound-media-into-sandbox-workspace.test.ts | 2 +- src/cli/cron-cli.test.ts | 2 +- src/cli/models-cli.test.ts | 2 +- ...s-routing-allowfrom-channels-whatsapp-allowfrom.test.ts | 4 ++-- src/discord/monitor.slash.test.ts | 2 +- src/gateway/server.agent.gateway-server-agent-b.test.ts | 2 +- src/gateway/server.auth.test.ts | 4 ++-- src/gateway/server.chat.gateway-server-chat-b.test.ts | 6 +++--- src/gateway/server.health.test.ts | 2 +- src/gateway/server.misc.test.ts | 2 +- src/gateway/server.models-voicewake.test.ts | 2 +- src/gateway/server.sessions-send.test.ts | 6 +++--- ...eb-auto-reply.reconnects-after-connection-close.test.ts | 2 +- src/web/logout.test.ts | 4 ++-- vitest.config.ts | 7 +++++-- 17 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/agents/sandbox-agent-config.agent-specific-sandbox-config.should-use-global-sandbox-config-no-agent.test.ts b/src/agents/sandbox-agent-config.agent-specific-sandbox-config.should-use-global-sandbox-config-no-agent.test.ts index 24e1ef4a9..b2d5a6a15 100644 --- a/src/agents/sandbox-agent-config.agent-specific-sandbox-config.should-use-global-sandbox-config-no-agent.test.ts +++ b/src/agents/sandbox-agent-config.agent-specific-sandbox-config.should-use-global-sandbox-config-no-agent.test.ts @@ -52,7 +52,7 @@ describe("Agent-specific sandbox config", () => { it( "should use global sandbox config when no agent-specific config exists", - { timeout: 15_000 }, + { timeout: 60_000 }, async () => { const { resolveSandboxContext } = await import("./sandbox.js"); diff --git a/src/agents/sandbox-merge.test.ts b/src/agents/sandbox-merge.test.ts index 10cdb44dc..8f3c7807e 100644 --- a/src/agents/sandbox-merge.test.ts +++ b/src/agents/sandbox-merge.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from "vitest"; describe("sandbox config merges", () => { - it("resolves sandbox scope deterministically", { timeout: 15_000 }, async () => { + it("resolves sandbox scope deterministically", { timeout: 60_000 }, async () => { const { resolveSandboxScope } = await import("./sandbox.js"); expect(resolveSandboxScope({})).toBe("agent"); diff --git a/src/auto-reply/reply.triggers.trigger-handling.stages-inbound-media-into-sandbox-workspace.test.ts b/src/auto-reply/reply.triggers.trigger-handling.stages-inbound-media-into-sandbox-workspace.test.ts index f3861b0b6..23e66f1a5 100644 --- a/src/auto-reply/reply.triggers.trigger-handling.stages-inbound-media-into-sandbox-workspace.test.ts +++ b/src/auto-reply/reply.triggers.trigger-handling.stages-inbound-media-into-sandbox-workspace.test.ts @@ -97,7 +97,7 @@ afterEach(() => { }); describe("trigger handling", () => { - it("stages inbound media into the sandbox workspace", { timeout: 15_000 }, async () => { + it("stages inbound media into the sandbox workspace", { timeout: 60_000 }, async () => { await withTempHome(async (home) => { const inboundDir = join(home, ".clawdbot", "media", "inbound"); await fs.mkdir(inboundDir, { recursive: true }); diff --git a/src/cli/cron-cli.test.ts b/src/cli/cron-cli.test.ts index 610dcde90..63fba4279 100644 --- a/src/cli/cron-cli.test.ts +++ b/src/cli/cron-cli.test.ts @@ -26,7 +26,7 @@ vi.mock("../runtime.js", () => ({ })); describe("cron cli", () => { - it("trims model and thinking on cron add", { timeout: 30_000 }, async () => { + it("trims model and thinking on cron add", { timeout: 60_000 }, async () => { callGatewayFromCli.mockClear(); const { registerCronCli } = await import("./cron-cli.js"); diff --git a/src/cli/models-cli.test.ts b/src/cli/models-cli.test.ts index 717cd08a8..ba7233874 100644 --- a/src/cli/models-cli.test.ts +++ b/src/cli/models-cli.test.ts @@ -14,7 +14,7 @@ vi.mock("../commands/models.js", async () => { }); describe("models cli", () => { - it("registers github-copilot login command", { timeout: 15_000 }, async () => { + it("registers github-copilot login command", { timeout: 60_000 }, async () => { const { Command } = await import("commander"); const { registerModelsCli } = await import("./models-cli.js"); diff --git a/src/commands/doctor.migrates-routing-allowfrom-channels-whatsapp-allowfrom.test.ts b/src/commands/doctor.migrates-routing-allowfrom-channels-whatsapp-allowfrom.test.ts index c4cf14c80..5262e4597 100644 --- a/src/commands/doctor.migrates-routing-allowfrom-channels-whatsapp-allowfrom.test.ts +++ b/src/commands/doctor.migrates-routing-allowfrom-channels-whatsapp-allowfrom.test.ts @@ -322,7 +322,7 @@ vi.mock("./doctor-state-migrations.js", () => ({ })); describe("doctor command", () => { - it("migrates routing.allowFrom to channels.whatsapp.allowFrom", { timeout: 30_000 }, async () => { + it("migrates routing.allowFrom to channels.whatsapp.allowFrom", { timeout: 60_000 }, async () => { readConfigFileSnapshot.mockResolvedValue({ path: "/tmp/clawdbot.json", exists: true, @@ -366,7 +366,7 @@ describe("doctor command", () => { expect(written.routing).toBeUndefined(); }); - it("migrates legacy gateway services", { timeout: 30_000 }, async () => { + it("migrates legacy gateway services", { timeout: 60_000 }, async () => { readConfigFileSnapshot.mockResolvedValue({ path: "/tmp/clawdbot.json", exists: true, diff --git a/src/discord/monitor.slash.test.ts b/src/discord/monitor.slash.test.ts index 48164b3d2..018f93ed0 100644 --- a/src/discord/monitor.slash.test.ts +++ b/src/discord/monitor.slash.test.ts @@ -33,7 +33,7 @@ beforeEach(() => { }); describe("discord native commands", () => { - it("streams tool results for native slash commands", { timeout: 30_000 }, async () => { + it("streams tool results for native slash commands", { timeout: 60_000 }, async () => { const { ChannelType } = await import("@buape/carbon"); const { createDiscordNativeCommand } = await import("./monitor.js"); diff --git a/src/gateway/server.agent.gateway-server-agent-b.test.ts b/src/gateway/server.agent.gateway-server-agent-b.test.ts index f3c5ecad8..ec5bea3e6 100644 --- a/src/gateway/server.agent.gateway-server-agent-b.test.ts +++ b/src/gateway/server.agent.gateway-server-agent-b.test.ts @@ -333,7 +333,7 @@ describe("gateway server agent", () => { await server.close(); }); - test("agent dedupe survives reconnect", { timeout: 15000 }, async () => { + test("agent dedupe survives reconnect", { timeout: 60_000 }, async () => { const port = await getFreePort(); const server = await startGatewayServer(port); diff --git a/src/gateway/server.auth.test.ts b/src/gateway/server.auth.test.ts index 29fd122d1..e6a20dc6b 100644 --- a/src/gateway/server.auth.test.ts +++ b/src/gateway/server.auth.test.ts @@ -26,7 +26,7 @@ async function waitForWsClose(ws: WebSocket, timeoutMs: number): Promise { - test("closes silent handshakes after timeout", { timeout: 30_000 }, async () => { + test("closes silent handshakes after timeout", { timeout: 60_000 }, async () => { vi.useRealTimers(); const { server, ws } = await startServerWithClient(); const closed = await waitForWsClose(ws, HANDSHAKE_TIMEOUT_MS + 2_000); @@ -129,7 +129,7 @@ describe("gateway server auth/connect", () => { test( "invalid connect params surface in response and close reason", - { timeout: 15000 }, + { timeout: 60_000 }, async () => { const { server, ws } = await startServerWithClient(); const closeInfoPromise = new Promise<{ code: number; reason: string }>((resolve) => { diff --git a/src/gateway/server.chat.gateway-server-chat-b.test.ts b/src/gateway/server.chat.gateway-server-chat-b.test.ts index 7f510b286..60bbce89f 100644 --- a/src/gateway/server.chat.gateway-server-chat-b.test.ts +++ b/src/gateway/server.chat.gateway-server-chat-b.test.ts @@ -26,7 +26,7 @@ async function waitFor(condition: () => boolean, timeoutMs = 1500) { } describe("gateway server chat", () => { - test("chat.history caps payload bytes", { timeout: 15_000 }, async () => { + test("chat.history caps payload bytes", { timeout: 60_000 }, async () => { const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); testState.sessionStorePath = path.join(dir, "sessions.json"); await writeSessionStore({ @@ -105,7 +105,7 @@ describe("gateway server chat", () => { await server.close(); }); - test("chat.abort cancels an in-flight chat.send", { timeout: 15000 }, async () => { + test("chat.abort cancels an in-flight chat.send", { timeout: 60_000 }, async () => { const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); testState.sessionStorePath = path.join(dir, "sessions.json"); await writeSessionStore({ @@ -263,7 +263,7 @@ describe("gateway server chat", () => { await server.close(); }); - test("chat.send treats /stop as an out-of-band abort", { timeout: 15000 }, async () => { + test("chat.send treats /stop as an out-of-band abort", { timeout: 60_000 }, async () => { const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); testState.sessionStorePath = path.join(dir, "sessions.json"); await writeSessionStore({ diff --git a/src/gateway/server.health.test.ts b/src/gateway/server.health.test.ts index 245b820f2..82a8713cc 100644 --- a/src/gateway/server.health.test.ts +++ b/src/gateway/server.health.test.ts @@ -16,7 +16,7 @@ import { installGatewayTestHooks(); describe("gateway server health/presence", () => { - test("connect + health + presence + status succeed", { timeout: 20_000 }, async () => { + test("connect + health + presence + status succeed", { timeout: 60_000 }, async () => { const { server, ws } = await startServerWithClient(); await connectOk(ws); diff --git a/src/gateway/server.misc.test.ts b/src/gateway/server.misc.test.ts index 644ff75ba..ede279d9f 100644 --- a/src/gateway/server.misc.test.ts +++ b/src/gateway/server.misc.test.ts @@ -46,7 +46,7 @@ describe("gateway server misc", () => { } }); - test("send dedupes by idempotencyKey", { timeout: 20_000 }, async () => { + test("send dedupes by idempotencyKey", { timeout: 60_000 }, async () => { const { server, ws } = await startServerWithClient(); await connectOk(ws); diff --git a/src/gateway/server.models-voicewake.test.ts b/src/gateway/server.models-voicewake.test.ts index 66bead812..2a4099a71 100644 --- a/src/gateway/server.models-voicewake.test.ts +++ b/src/gateway/server.models-voicewake.test.ts @@ -64,7 +64,7 @@ describe("gateway server models + voicewake", () => { test( "voicewake.get returns defaults and voicewake.set broadcasts", - { timeout: 30_000 }, + { timeout: 60_000 }, async () => { const homeDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-home-")); const restoreHome = setTempHome(homeDir); diff --git a/src/gateway/server.sessions-send.test.ts b/src/gateway/server.sessions-send.test.ts index 30ee07197..f9ed6402d 100644 --- a/src/gateway/server.sessions-send.test.ts +++ b/src/gateway/server.sessions-send.test.ts @@ -95,7 +95,7 @@ describe("sessions_send gateway loopback", () => { }); describe("sessions_send label lookup", () => { - it("finds session by label and sends message", { timeout: 15_000 }, async () => { + it("finds session by label and sends message", { timeout: 60_000 }, async () => { const port = await getFreePort(); const prevPort = process.env.CLAWDBOT_GATEWAY_PORT; process.env.CLAWDBOT_GATEWAY_PORT = String(port); @@ -170,7 +170,7 @@ describe("sessions_send label lookup", () => { } }); - it("returns error when label not found", { timeout: 15_000 }, async () => { + it("returns error when label not found", { timeout: 60_000 }, async () => { const port = await getFreePort(); const prevPort = process.env.CLAWDBOT_GATEWAY_PORT; process.env.CLAWDBOT_GATEWAY_PORT = String(port); @@ -199,7 +199,7 @@ describe("sessions_send label lookup", () => { } }); - it("returns error when neither sessionKey nor label provided", { timeout: 15_000 }, async () => { + it("returns error when neither sessionKey nor label provided", { timeout: 60_000 }, async () => { const port = await getFreePort(); const prevPort = process.env.CLAWDBOT_GATEWAY_PORT; process.env.CLAWDBOT_GATEWAY_PORT = String(port); diff --git a/src/web/auto-reply.web-auto-reply.reconnects-after-connection-close.test.ts b/src/web/auto-reply.web-auto-reply.reconnects-after-connection-close.test.ts index 5544926bb..c12d5284d 100644 --- a/src/web/auto-reply.web-auto-reply.reconnects-after-connection-close.test.ts +++ b/src/web/auto-reply.web-auto-reply.reconnects-after-connection-close.test.ts @@ -228,7 +228,7 @@ describe("web auto-reply", () => { await run; }, 15_000); - it("stops after hitting max reconnect attempts", { timeout: 20000 }, async () => { + it("stops after hitting max reconnect attempts", { timeout: 60_000 }, async () => { const closeResolvers: Array<() => void> = []; const sleep = vi.fn(async () => {}); const listenerFactory = vi.fn(async () => { diff --git a/src/web/logout.test.ts b/src/web/logout.test.ts index fdc51cac3..93b7a2a29 100644 --- a/src/web/logout.test.ts +++ b/src/web/logout.test.ts @@ -21,7 +21,7 @@ describe("web logout", () => { vi.restoreAllMocks(); }); - it("deletes cached credentials when present", { timeout: 15_000 }, async () => { + it("deletes cached credentials when present", { timeout: 60_000 }, async () => { await withTempHome(async (home) => { vi.resetModules(); const { logoutWeb, WA_WEB_AUTH_DIR } = await import("./session.js"); @@ -37,7 +37,7 @@ describe("web logout", () => { }); }); - it("no-ops when nothing to delete", { timeout: 15_000 }, async () => { + it("no-ops when nothing to delete", { timeout: 60_000 }, async () => { await withTempHome(async () => { vi.resetModules(); const { logoutWeb } = await import("./session.js"); diff --git a/vitest.config.ts b/vitest.config.ts index fd42520f4..752434dd1 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -3,6 +3,7 @@ import { fileURLToPath } from "node:url"; import { defineConfig } from "vitest/config"; const repoRoot = path.dirname(fileURLToPath(import.meta.url)); +const isCI = process.env.CI === "true" || process.env.GITHUB_ACTIONS === "true"; export default defineConfig({ resolve: { @@ -11,8 +12,10 @@ export default defineConfig({ }, }, test: { - testTimeout: 30_000, - hookTimeout: 60_000, + testTimeout: 60_000, + hookTimeout: 120_000, + pool: "forks", + maxWorkers: isCI ? 3 : 4, include: [ "src/**/*.test.ts", "extensions/**/*.test.ts",