From 3dd19a17053ebb009e97061e394912c16b82bf99 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 8 Apr 2026 14:12:50 +0100 Subject: [PATCH] refactor: dedupe test helpers and script warning filter --- scripts/process-warning-filter.mjs | 32 ++++++ ...test-built-bundled-channel-entry-smoke.mjs | 34 +----- scripts/test-built-plugin-singleton.mjs | 34 +----- src/agents/cli-backends.test.ts | 108 ++++++++---------- src/gateway/server-methods/devices.test.ts | 97 ++++++---------- 5 files changed, 115 insertions(+), 190 deletions(-) create mode 100644 scripts/process-warning-filter.mjs diff --git a/scripts/process-warning-filter.mjs b/scripts/process-warning-filter.mjs new file mode 100644 index 00000000000..f79b3b6b62c --- /dev/null +++ b/scripts/process-warning-filter.mjs @@ -0,0 +1,32 @@ +const warningFilterKey = Symbol.for("openclaw.warning-filter"); + +export function installProcessWarningFilter() { + if (globalThis[warningFilterKey]?.installed) { + return; + } + + const originalEmitWarning = process.emitWarning.bind(process); + process.emitWarning = (...args) => { + const [warningArg, secondArg, thirdArg] = args; + const warning = + warningArg instanceof Error + ? { + name: warningArg.name, + message: warningArg.message, + code: warningArg.code, + } + : { + name: typeof secondArg === "string" ? secondArg : secondArg?.type, + message: typeof warningArg === "string" ? warningArg : undefined, + code: typeof thirdArg === "string" ? thirdArg : secondArg?.code, + }; + + if (warning.code === "DEP0040" && warning.message?.includes("punycode")) { + return; + } + + return Reflect.apply(originalEmitWarning, process, args); + }; + + globalThis[warningFilterKey] = { installed: true }; +} diff --git a/scripts/test-built-bundled-channel-entry-smoke.mjs b/scripts/test-built-bundled-channel-entry-smoke.mjs index b5439bddfdc..db636ce910d 100644 --- a/scripts/test-built-bundled-channel-entry-smoke.mjs +++ b/scripts/test-built-bundled-channel-entry-smoke.mjs @@ -2,39 +2,7 @@ import assert from "node:assert/strict"; import fs from "node:fs"; import path from "node:path"; import { fileURLToPath, pathToFileURL } from "node:url"; - -const warningFilterKey = Symbol.for("openclaw.warning-filter"); - -function installProcessWarningFilter() { - if (globalThis[warningFilterKey]?.installed) { - return; - } - - const originalEmitWarning = process.emitWarning.bind(process); - process.emitWarning = (...args) => { - const [warningArg, secondArg, thirdArg] = args; - const warning = - warningArg instanceof Error - ? { - name: warningArg.name, - message: warningArg.message, - code: warningArg.code, - } - : { - name: typeof secondArg === "string" ? secondArg : secondArg?.type, - message: typeof warningArg === "string" ? warningArg : undefined, - code: typeof thirdArg === "string" ? thirdArg : secondArg?.code, - }; - - if (warning.code === "DEP0040" && warning.message?.includes("punycode")) { - return; - } - - return Reflect.apply(originalEmitWarning, process, args); - }; - - globalThis[warningFilterKey] = { installed: true }; -} +import { installProcessWarningFilter } from "./process-warning-filter.mjs"; installProcessWarningFilter(); diff --git a/scripts/test-built-plugin-singleton.mjs b/scripts/test-built-plugin-singleton.mjs index 5f75bc9c0ae..e97167cbd10 100644 --- a/scripts/test-built-plugin-singleton.mjs +++ b/scripts/test-built-plugin-singleton.mjs @@ -3,41 +3,9 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { fileURLToPath, pathToFileURL } from "node:url"; +import { installProcessWarningFilter } from "./process-warning-filter.mjs"; import { stageBundledPluginRuntime } from "./stage-bundled-plugin-runtime.mjs"; -const warningFilterKey = Symbol.for("openclaw.warning-filter"); - -function installProcessWarningFilter() { - if (globalThis[warningFilterKey]?.installed) { - return; - } - - const originalEmitWarning = process.emitWarning.bind(process); - process.emitWarning = (...args) => { - const [warningArg, secondArg, thirdArg] = args; - const warning = - warningArg instanceof Error - ? { - name: warningArg.name, - message: warningArg.message, - code: warningArg.code, - } - : { - name: typeof secondArg === "string" ? secondArg : secondArg?.type, - message: typeof warningArg === "string" ? warningArg : undefined, - code: typeof thirdArg === "string" ? thirdArg : secondArg?.code, - }; - - if (warning.code === "DEP0040" && warning.message?.includes("punycode")) { - return; - } - - return Reflect.apply(originalEmitWarning, process, args); - }; - - globalThis[warningFilterKey] = { installed: true }; -} - installProcessWarningFilter(); const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); diff --git a/src/agents/cli-backends.test.ts b/src/agents/cli-backends.test.ts index fc88399c57f..26554069deb 100644 --- a/src/agents/cli-backends.test.ts +++ b/src/agents/cli-backends.test.ts @@ -60,6 +60,38 @@ function createBackendEntry(params: { }; } +function createClaudeCliOverrideConfig(config: CliBackendConfig): OpenClawConfig { + return { + agents: { + defaults: { + cliBackends: { + "claude-cli": config, + }, + }, + }, + } satisfies OpenClawConfig; +} + +const NORMALIZED_CLAUDE_FALLBACK_ARGS = [ + "-p", + "--output-format", + "stream-json", + "--setting-sources", + "user", + "--permission-mode", + "bypassPermissions", +]; + +const NORMALIZED_CLAUDE_FALLBACK_RESUME_ARGS = [ + "-p", + "--resume", + "{sessionId}", + "--setting-sources", + "user", + "--permission-mode", + "bypassPermissions", +]; + beforeAll(async () => { vi.doUnmock("../plugins/setup-registry.js"); vi.doUnmock("../plugins/cli-backends.runtime.js"); @@ -449,79 +481,31 @@ describe("resolveCliBackendConfig claude-cli defaults", () => { }); it("falls back to user-only setting sources when a custom override leaves the flag without a value", () => { - const cfg = { - agents: { - defaults: { - cliBackends: { - "claude-cli": { - command: "claude", - args: ["-p", "--setting-sources", "--output-format", "stream-json"], - resumeArgs: ["-p", "--setting-sources", "--resume", "{sessionId}"], - }, - }, - }, - }, - } satisfies OpenClawConfig; + const cfg = createClaudeCliOverrideConfig({ + command: "claude", + args: ["-p", "--setting-sources", "--output-format", "stream-json"], + resumeArgs: ["-p", "--setting-sources", "--resume", "{sessionId}"], + }); const resolved = resolveCliBackendConfig("claude-cli", cfg); expect(resolved).not.toBeNull(); - expect(resolved?.config.args).toEqual([ - "-p", - "--output-format", - "stream-json", - "--setting-sources", - "user", - "--permission-mode", - "bypassPermissions", - ]); - expect(resolved?.config.resumeArgs).toEqual([ - "-p", - "--resume", - "{sessionId}", - "--setting-sources", - "user", - "--permission-mode", - "bypassPermissions", - ]); + expect(resolved?.config.args).toEqual(NORMALIZED_CLAUDE_FALLBACK_ARGS); + expect(resolved?.config.resumeArgs).toEqual(NORMALIZED_CLAUDE_FALLBACK_RESUME_ARGS); }); it("falls back to bypassPermissions when a custom override leaves permission-mode without a value", () => { - const cfg = { - agents: { - defaults: { - cliBackends: { - "claude-cli": { - command: "claude", - args: ["-p", "--permission-mode", "--output-format", "stream-json"], - resumeArgs: ["-p", "--permission-mode", "--resume", "{sessionId}"], - }, - }, - }, - }, - } satisfies OpenClawConfig; + const cfg = createClaudeCliOverrideConfig({ + command: "claude", + args: ["-p", "--permission-mode", "--output-format", "stream-json"], + resumeArgs: ["-p", "--permission-mode", "--resume", "{sessionId}"], + }); const resolved = resolveCliBackendConfig("claude-cli", cfg); expect(resolved).not.toBeNull(); - expect(resolved?.config.args).toEqual([ - "-p", - "--output-format", - "stream-json", - "--setting-sources", - "user", - "--permission-mode", - "bypassPermissions", - ]); - expect(resolved?.config.resumeArgs).toEqual([ - "-p", - "--resume", - "{sessionId}", - "--setting-sources", - "user", - "--permission-mode", - "bypassPermissions", - ]); + expect(resolved?.config.args).toEqual(NORMALIZED_CLAUDE_FALLBACK_ARGS); + expect(resolved?.config.resumeArgs).toEqual(NORMALIZED_CLAUDE_FALLBACK_RESUME_ARGS); }); it("injects bypassPermissions when custom args omit any permission flag", () => { diff --git a/src/gateway/server-methods/devices.test.ts b/src/gateway/server-methods/devices.test.ts index 3f7028f7c05..a8e1c1a98e9 100644 --- a/src/gateway/server-methods/devices.test.ts +++ b/src/gateway/server-methods/devices.test.ts @@ -60,6 +60,36 @@ function createOptions( } as unknown as GatewayRequestHandlerOptions; } +function mockPairedOperatorDevice(): void { + getPairedDeviceMock.mockResolvedValue({ + deviceId: "device-1", + role: "operator", + roles: ["operator"], + scopes: ["operator.pairing"], + tokens: { + operator: { + token: "old-token", + role: "operator", + scopes: ["operator.pairing"], + createdAtMs: 123, + }, + }, + }); +} + +function mockRotateOperatorTokenSuccess(): void { + rotateDeviceTokenMock.mockResolvedValue({ + ok: true, + entry: { + token: "new-token", + role: "operator", + scopes: ["operator.pairing"], + createdAtMs: 456, + rotatedAtMs: 789, + }, + }); +} + describe("deviceHandlers", () => { beforeEach(() => { vi.clearAllMocks(); @@ -197,30 +227,8 @@ describe("deviceHandlers", () => { }); it("disconnects active clients after rotating a device token", async () => { - getPairedDeviceMock.mockResolvedValue({ - deviceId: "device-1", - role: "operator", - roles: ["operator"], - scopes: ["operator.pairing"], - tokens: { - operator: { - token: "old-token", - role: "operator", - scopes: ["operator.pairing"], - createdAtMs: 123, - }, - }, - }); - rotateDeviceTokenMock.mockResolvedValue({ - ok: true, - entry: { - token: "new-token", - role: "operator", - scopes: ["operator.pairing"], - createdAtMs: 456, - rotatedAtMs: 789, - }, - }); + mockPairedOperatorDevice(); + mockRotateOperatorTokenSuccess(); const opts = createOptions( "device.token.rotate", { @@ -262,30 +270,8 @@ describe("deviceHandlers", () => { }); it("treats normalized device ids as self-owned for token rotation", async () => { - getPairedDeviceMock.mockResolvedValue({ - deviceId: "device-1", - role: "operator", - roles: ["operator"], - scopes: ["operator.pairing"], - tokens: { - operator: { - token: "old-token", - role: "operator", - scopes: ["operator.pairing"], - createdAtMs: 123, - }, - }, - }); - rotateDeviceTokenMock.mockResolvedValue({ - ok: true, - entry: { - token: "new-token", - role: "operator", - scopes: ["operator.pairing"], - createdAtMs: 456, - rotatedAtMs: 789, - }, - }); + mockPairedOperatorDevice(); + mockRotateOperatorTokenSuccess(); const opts = createOptions( "device.token.rotate", { @@ -317,20 +303,7 @@ describe("deviceHandlers", () => { }); it("rejects rotating a token for a role that was never approved", async () => { - getPairedDeviceMock.mockResolvedValue({ - deviceId: "device-1", - role: "operator", - roles: ["operator"], - scopes: ["operator.pairing"], - tokens: { - operator: { - token: "old-token", - role: "operator", - scopes: ["operator.pairing"], - createdAtMs: 123, - }, - }, - }); + mockPairedOperatorDevice(); const opts = createOptions( "device.token.rotate", {