From 5faba6a48cbda0bb394b6cce1e4e04af08503133 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 14 Feb 2026 17:56:50 +0000 Subject: [PATCH] refactor(test): reuse web auto-reply harness in more tests --- ...onnects-after-connection-close.e2e.test.ts | 111 ++--------------- ...-activation-silent-token-preserves.test.ts | 112 ++---------------- 2 files changed, 20 insertions(+), 203 deletions(-) diff --git a/src/web/auto-reply.web-auto-reply.reconnects-after-connection-close.e2e.test.ts b/src/web/auto-reply.web-auto-reply.reconnects-after-connection-close.e2e.test.ts index 3abb088f58..07c0365e76 100644 --- a/src/web/auto-reply.web-auto-reply.reconnects-after-connection-close.e2e.test.ts +++ b/src/web/auto-reply.web-auto-reply.reconnects-after-connection-close.e2e.test.ts @@ -1,110 +1,17 @@ -import "./test-helpers.js"; -import fs from "node:fs/promises"; -import os from "node:os"; -import path from "node:path"; -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { describe, expect, it, vi } from "vitest"; import { escapeRegExp, formatEnvelopeTimestamp } from "../../test/helpers/envelope-timestamp.js"; - -vi.mock("../agents/pi-embedded.js", () => ({ - abortEmbeddedPiRun: vi.fn().mockReturnValue(false), - isEmbeddedPiRunActive: vi.fn().mockReturnValue(false), - isEmbeddedPiRunStreaming: vi.fn().mockReturnValue(false), - runEmbeddedPiAgent: vi.fn(), - queueEmbeddedPiMessage: vi.fn().mockReturnValue(false), - resolveEmbeddedSessionLane: (key: string) => `session:${key.trim() || "main"}`, -})); - -import { resetInboundDedupe } from "../auto-reply/reply/inbound-dedupe.js"; -import { resetLogger, setLoggerOverride } from "../logging.js"; import { monitorWebChannel } from "./auto-reply.js"; -import { resetBaileysMocks, resetLoadConfigMock, setLoadConfigMock } from "./test-helpers.js"; +import { + installWebAutoReplyTestHomeHooks, + installWebAutoReplyUnitTestHooks, + makeSessionStore, + setLoadConfigMock, +} from "./auto-reply.test-harness.js"; -let previousHome: string | undefined; -let tempHome: string | undefined; - -const rmDirWithRetries = async (dir: string): Promise => { - // Some tests can leave async session-store writes in-flight; recursive deletion can race and throw ENOTEMPTY. - for (let attempt = 0; attempt < 10; attempt += 1) { - try { - await fs.rm(dir, { recursive: true, force: true }); - return; - } catch (err) { - const code = - err && typeof err === "object" && "code" in err - ? String((err as { code?: unknown }).code) - : null; - if (code === "ENOTEMPTY" || code === "EBUSY" || code === "EPERM") { - await new Promise((resolve) => setTimeout(resolve, 5)); - continue; - } - throw err; - } - } - - await fs.rm(dir, { recursive: true, force: true }); -}; - -beforeEach(async () => { - resetInboundDedupe(); - previousHome = process.env.HOME; - tempHome = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-web-home-")); - process.env.HOME = tempHome; -}); - -afterEach(async () => { - process.env.HOME = previousHome; - if (tempHome) { - await rmDirWithRetries(tempHome); - tempHome = undefined; - } -}); - -const makeSessionStore = async ( - entries: Record = {}, -): Promise<{ storePath: string; cleanup: () => Promise }> => { - const dir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-session-")); - const storePath = path.join(dir, "sessions.json"); - await fs.writeFile(storePath, JSON.stringify(entries)); - const cleanup = async () => { - // Session store writes can be in-flight when the test finishes (e.g. updateLastRoute - // after a message flush). `fs.rm({ recursive })` can race and throw ENOTEMPTY. - for (let attempt = 0; attempt < 10; attempt += 1) { - try { - await fs.rm(dir, { recursive: true, force: true }); - return; - } catch (err) { - const code = - err && typeof err === "object" && "code" in err - ? String((err as { code?: unknown }).code) - : null; - if (code === "ENOTEMPTY" || code === "EBUSY" || code === "EPERM") { - await new Promise((resolve) => setTimeout(resolve, 5)); - continue; - } - throw err; - } - } - - await fs.rm(dir, { recursive: true, force: true }); - }; - return { - storePath, - cleanup, - }; -}; +installWebAutoReplyTestHomeHooks(); describe("web auto-reply", () => { - beforeEach(() => { - vi.clearAllMocks(); - resetBaileysMocks(); - resetLoadConfigMock(); - }); - - afterEach(() => { - resetLogger(); - setLoggerOverride(null); - vi.useRealTimers(); - }); + installWebAutoReplyUnitTestHooks(); it("handles helper envelope timestamps with trimmed timezones (regression)", () => { const d = new Date("2025-01-01T00:00:00.000Z"); diff --git a/src/web/auto-reply.web-auto-reply.supports-always-group-activation-silent-token-preserves.test.ts b/src/web/auto-reply.web-auto-reply.supports-always-group-activation-silent-token-preserves.test.ts index fe7af6808c..2e82d400f4 100644 --- a/src/web/auto-reply.web-auto-reply.supports-always-group-activation-silent-token-preserves.test.ts +++ b/src/web/auto-reply.web-auto-reply.supports-always-group-activation-silent-token-preserves.test.ts @@ -1,111 +1,21 @@ -import "./test-helpers.js"; import crypto from "node:crypto"; import fs from "node:fs/promises"; -import os from "node:os"; -import path from "node:path"; -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; - -vi.mock("../agents/pi-embedded.js", () => ({ - abortEmbeddedPiRun: vi.fn().mockReturnValue(false), - isEmbeddedPiRunActive: vi.fn().mockReturnValue(false), - isEmbeddedPiRunStreaming: vi.fn().mockReturnValue(false), - runEmbeddedPiAgent: vi.fn(), - queueEmbeddedPiMessage: vi.fn().mockReturnValue(false), - resolveEmbeddedSessionLane: (key: string) => `session:${key.trim() || "main"}`, -})); - +import { describe, expect, it, vi } from "vitest"; import { expectInboundContextContract } from "../../test/helpers/inbound-contract.js"; -import { resetInboundDedupe } from "../auto-reply/reply/inbound-dedupe.js"; -import { resetLogger, setLoggerOverride } from "../logging.js"; +import { setLoggerOverride } from "../logging.js"; import { monitorWebChannel, SILENT_REPLY_TOKEN } from "./auto-reply.js"; -import { resetBaileysMocks, resetLoadConfigMock, setLoadConfigMock } from "./test-helpers.js"; +import { + installWebAutoReplyTestHomeHooks, + installWebAutoReplyUnitTestHooks, + makeSessionStore, + resetLoadConfigMock, + setLoadConfigMock, +} from "./auto-reply.test-harness.js"; -let previousHome: string | undefined; -let tempHome: string | undefined; - -const rmDirWithRetries = async (dir: string): Promise => { - // Some tests can leave async session-store writes in-flight; recursive deletion can race and throw ENOTEMPTY. - for (let attempt = 0; attempt < 10; attempt += 1) { - try { - await fs.rm(dir, { recursive: true, force: true }); - return; - } catch (err) { - const code = - err && typeof err === "object" && "code" in err - ? String((err as { code?: unknown }).code) - : null; - if (code === "ENOTEMPTY" || code === "EBUSY" || code === "EPERM") { - await new Promise((resolve) => setTimeout(resolve, 5)); - continue; - } - throw err; - } - } - - await fs.rm(dir, { recursive: true, force: true }); -}; - -beforeEach(async () => { - resetInboundDedupe(); - previousHome = process.env.HOME; - tempHome = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-web-home-")); - process.env.HOME = tempHome; -}); - -afterEach(async () => { - process.env.HOME = previousHome; - if (tempHome) { - await rmDirWithRetries(tempHome); - tempHome = undefined; - } -}); - -const makeSessionStore = async ( - entries: Record = {}, -): Promise<{ storePath: string; cleanup: () => Promise }> => { - const dir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-session-")); - const storePath = path.join(dir, "sessions.json"); - await fs.writeFile(storePath, JSON.stringify(entries)); - const cleanup = async () => { - // Session store writes can be in-flight when the test finishes (e.g. updateLastRoute - // after a message flush). `fs.rm({ recursive })` can race and throw ENOTEMPTY. - for (let attempt = 0; attempt < 10; attempt += 1) { - try { - await fs.rm(dir, { recursive: true, force: true }); - return; - } catch (err) { - const code = - err && typeof err === "object" && "code" in err - ? String((err as { code?: unknown }).code) - : null; - if (code === "ENOTEMPTY" || code === "EBUSY" || code === "EPERM") { - await new Promise((resolve) => setTimeout(resolve, 5)); - continue; - } - throw err; - } - } - - await fs.rm(dir, { recursive: true, force: true }); - }; - return { - storePath, - cleanup, - }; -}; +installWebAutoReplyTestHomeHooks(); describe("web auto-reply", () => { - beforeEach(() => { - vi.clearAllMocks(); - resetBaileysMocks(); - resetLoadConfigMock(); - }); - - afterEach(() => { - resetLogger(); - setLoggerOverride(null); - vi.useRealTimers(); - }); + installWebAutoReplyUnitTestHooks(); it("supports always-on group activation with silent token and clears pending history", async () => { const sendMedia = vi.fn();