mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-03 09:30:21 +00:00
test: harden shared-worker runtime setup
This commit is contained in:
@@ -12,12 +12,19 @@ const hookRunnerMocks = vi.hoisted(() => ({
|
||||
runBeforeReset: vi.fn<HookRunner["runBeforeReset"]>(),
|
||||
}));
|
||||
|
||||
vi.mock("node:fs/promises", () => ({
|
||||
default: {
|
||||
vi.mock("node:fs/promises", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("node:fs/promises")>();
|
||||
return {
|
||||
...actual,
|
||||
default: {
|
||||
...actual,
|
||||
readFile: fsMocks.readFile,
|
||||
readdir: fsMocks.readdir,
|
||||
},
|
||||
readFile: fsMocks.readFile,
|
||||
readdir: fsMocks.readdir,
|
||||
},
|
||||
}));
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("../../plugins/hook-runner-global.js", () => ({
|
||||
getGlobalHookRunner: () =>
|
||||
|
||||
@@ -3,9 +3,13 @@ import fs from "node:fs/promises";
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
import { prepareRestartScript, runRestartScript } from "./restart-helper.js";
|
||||
|
||||
vi.mock("node:child_process", () => ({
|
||||
spawn: vi.fn(),
|
||||
}));
|
||||
vi.mock("node:child_process", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("node:child_process")>();
|
||||
return {
|
||||
...actual,
|
||||
spawn: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
describe("restart-helper", () => {
|
||||
const originalPlatform = process.platform;
|
||||
|
||||
@@ -3,9 +3,13 @@ import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
const spawnMock = vi.hoisted(() => vi.fn());
|
||||
const unrefMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock("node:child_process", () => ({
|
||||
spawn: (...args: unknown[]) => spawnMock(...args),
|
||||
}));
|
||||
vi.mock("node:child_process", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("node:child_process")>();
|
||||
return {
|
||||
...actual,
|
||||
spawn: (...args: unknown[]) => spawnMock(...args),
|
||||
};
|
||||
});
|
||||
|
||||
import { scheduleDetachedLaunchdRestartHandoff } from "./launchd-restart-handoff.js";
|
||||
|
||||
|
||||
@@ -10,15 +10,27 @@ const fsMocks = vi.hoisted(() => ({
|
||||
realpath: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("node:fs/promises", () => ({
|
||||
default: { access: fsMocks.access, realpath: fsMocks.realpath },
|
||||
access: fsMocks.access,
|
||||
realpath: fsMocks.realpath,
|
||||
}));
|
||||
vi.mock("node:fs/promises", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("node:fs/promises")>();
|
||||
return {
|
||||
...actual,
|
||||
default: {
|
||||
...actual,
|
||||
access: fsMocks.access,
|
||||
realpath: fsMocks.realpath,
|
||||
},
|
||||
access: fsMocks.access,
|
||||
realpath: fsMocks.realpath,
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("node:child_process", () => ({
|
||||
execFileSync: childProcessMocks.execFileSync,
|
||||
}));
|
||||
vi.mock("node:child_process", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("node:child_process")>();
|
||||
return {
|
||||
...actual,
|
||||
execFileSync: childProcessMocks.execFileSync,
|
||||
};
|
||||
});
|
||||
|
||||
import { resolveGatewayProgramArguments } from "./program-args.js";
|
||||
|
||||
|
||||
@@ -4,10 +4,17 @@ const fsMocks = vi.hoisted(() => ({
|
||||
access: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("node:fs/promises", () => ({
|
||||
default: { access: fsMocks.access },
|
||||
access: fsMocks.access,
|
||||
}));
|
||||
vi.mock("node:fs/promises", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("node:fs/promises")>();
|
||||
return {
|
||||
...actual,
|
||||
default: {
|
||||
...actual,
|
||||
access: fsMocks.access,
|
||||
},
|
||||
access: fsMocks.access,
|
||||
};
|
||||
});
|
||||
|
||||
import {
|
||||
renderSystemNodeWarning,
|
||||
|
||||
@@ -4,9 +4,13 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const execFileMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock("node:child_process", () => ({
|
||||
execFile: execFileMock,
|
||||
}));
|
||||
vi.mock("node:child_process", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("node:child_process")>();
|
||||
return {
|
||||
...actual,
|
||||
execFile: execFileMock,
|
||||
};
|
||||
});
|
||||
|
||||
import { splitArgsPreservingQuotes } from "./arg-split.js";
|
||||
import { parseSystemdExecStart } from "./systemd-unit.js";
|
||||
|
||||
@@ -3,9 +3,13 @@ import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { configHandlers, resolveConfigOpenCommand } from "./config.js";
|
||||
import type { GatewayRequestHandlerOptions } from "./types.js";
|
||||
|
||||
vi.mock("node:child_process", () => ({
|
||||
execFile: vi.fn(),
|
||||
}));
|
||||
vi.mock("node:child_process", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("node:child_process")>();
|
||||
return {
|
||||
...actual,
|
||||
execFile: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
function invokeExecFileCallback(args: unknown[], error: Error | null) {
|
||||
const callback = args.at(-1);
|
||||
|
||||
@@ -7,15 +7,25 @@ const parseProcCmdlineMock = vi.hoisted(() => vi.fn());
|
||||
const isGatewayArgvMock = vi.hoisted(() => vi.fn());
|
||||
const findGatewayPidsOnPortSyncMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock("node:child_process", () => ({
|
||||
spawnSync: (...args: unknown[]) => spawnSyncMock(...args),
|
||||
}));
|
||||
vi.mock("node:child_process", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("node:child_process")>();
|
||||
return {
|
||||
...actual,
|
||||
spawnSync: (...args: unknown[]) => spawnSyncMock(...args),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("node:fs", () => ({
|
||||
default: {
|
||||
vi.mock("node:fs", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("node:fs")>();
|
||||
return {
|
||||
...actual,
|
||||
default: {
|
||||
...actual,
|
||||
readFileSync: (...args: unknown[]) => readFileSyncMock(...args),
|
||||
},
|
||||
readFileSync: (...args: unknown[]) => readFileSyncMock(...args),
|
||||
},
|
||||
}));
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("../daemon/cmd-argv.js", () => ({
|
||||
parseCmdScriptCommandLine: (...args: unknown[]) => parseCmdScriptCommandLineMock(...args),
|
||||
@@ -30,6 +40,26 @@ vi.mock("./restart-stale-pids.js", () => ({
|
||||
findGatewayPidsOnPortSync: (...args: unknown[]) => findGatewayPidsOnPortSyncMock(...args),
|
||||
}));
|
||||
|
||||
vi.mock("../logging/subsystem.js", () => ({
|
||||
createSubsystemLogger: vi.fn(() => ({
|
||||
warn: vi.fn(),
|
||||
info: vi.fn(),
|
||||
error: vi.fn(),
|
||||
debug: vi.fn(),
|
||||
trace: vi.fn(),
|
||||
fatal: vi.fn(),
|
||||
raw: vi.fn(),
|
||||
child: vi.fn(),
|
||||
isEnabled: vi.fn(() => false),
|
||||
subsystem: "test",
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock("../channels/chat-meta.js", () => ({
|
||||
listChatChannels: vi.fn(() => []),
|
||||
getChatChannelMeta: vi.fn(() => null),
|
||||
}));
|
||||
|
||||
const {
|
||||
findVerifiedGatewayListenerPidsOnPortSync,
|
||||
formatGatewayPidList,
|
||||
|
||||
@@ -3,7 +3,6 @@ import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { HEARTBEAT_PROMPT } from "../auto-reply/heartbeat.js";
|
||||
import type { ChannelOutboundAdapter } from "../channels/plugins/types.js";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import {
|
||||
resolveAgentIdFromSessionKey,
|
||||
@@ -11,9 +10,9 @@ import {
|
||||
resolveMainSessionKey,
|
||||
resolveStorePath,
|
||||
} from "../config/sessions.js";
|
||||
import { isWhatsAppGroupJid, normalizeWhatsAppTarget } from "../plugin-sdk/whatsapp-targets.js";
|
||||
import { getActivePluginRegistry, setActivePluginRegistry } from "../plugins/runtime.js";
|
||||
import { buildAgentPeerSessionKey } from "../routing/session-key.js";
|
||||
import { loadBundledPluginTestApiSync } from "../test-utils/bundled-plugin-public-surface.js";
|
||||
import { createOutboundTestPlugin, createTestRegistry } from "../test-utils/channel-plugins.js";
|
||||
import { typedCases } from "../test-utils/typed-cases.js";
|
||||
import {
|
||||
@@ -23,6 +22,7 @@ import {
|
||||
resolveHeartbeatPrompt,
|
||||
runHeartbeatOnce,
|
||||
} from "./heartbeat-runner.js";
|
||||
import { whatsappOutbound } from "./outbound/deliver.test-outbounds.js";
|
||||
import {
|
||||
resolveHeartbeatDeliveryTarget,
|
||||
resolveHeartbeatSenderContext,
|
||||
@@ -35,15 +35,40 @@ let testRegistry: ReturnType<typeof getActivePluginRegistry> | null = null;
|
||||
|
||||
let fixtureRoot = "";
|
||||
let fixtureCount = 0;
|
||||
let whatsappOutboundCache: ChannelOutboundAdapter | undefined;
|
||||
|
||||
function getWhatsAppOutbound(): ChannelOutboundAdapter {
|
||||
if (!whatsappOutboundCache) {
|
||||
({ whatsappOutbound: whatsappOutboundCache } = loadBundledPluginTestApiSync<{
|
||||
whatsappOutbound: ChannelOutboundAdapter;
|
||||
}>("whatsapp"));
|
||||
function resolveWhatsAppTargetForTest(params: {
|
||||
to: string | null | undefined;
|
||||
allowFrom: Array<string | number> | null | undefined;
|
||||
}) {
|
||||
const trimmed = params.to?.trim() ?? "";
|
||||
const allowListRaw = (params.allowFrom ?? [])
|
||||
.map((entry) => String(entry).trim())
|
||||
.filter(Boolean);
|
||||
const hasWildcard = allowListRaw.includes("*");
|
||||
const allowList = allowListRaw
|
||||
.filter((entry) => entry !== "*")
|
||||
.map((entry) => normalizeWhatsAppTarget(entry))
|
||||
.filter((entry): entry is string => Boolean(entry));
|
||||
const normalizedTarget = normalizeWhatsAppTarget(trimmed);
|
||||
|
||||
if (!normalizedTarget) {
|
||||
return {
|
||||
ok: false as const,
|
||||
error: new Error('Missing target for WhatsApp; expected "<E.164|group JID>".'),
|
||||
};
|
||||
}
|
||||
return whatsappOutboundCache;
|
||||
if (isWhatsAppGroupJid(normalizedTarget)) {
|
||||
return { ok: true as const, to: normalizedTarget };
|
||||
}
|
||||
if (hasWildcard || allowList.length === 0 || allowList.includes(normalizedTarget)) {
|
||||
return { ok: true as const, to: normalizedTarget };
|
||||
}
|
||||
return {
|
||||
ok: false as const,
|
||||
error: new Error(
|
||||
`Target "${normalizedTarget}" is not listed in the configured WhatsApp allowFrom policy.`,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
const createCaseDir = async (prefix: string) => {
|
||||
@@ -57,7 +82,14 @@ beforeAll(async () => {
|
||||
|
||||
const whatsappPlugin = createOutboundTestPlugin({
|
||||
id: "whatsapp",
|
||||
outbound: getWhatsAppOutbound(),
|
||||
outbound: {
|
||||
...whatsappOutbound,
|
||||
resolveTarget: ({ to, allowFrom }) =>
|
||||
resolveWhatsAppTargetForTest({
|
||||
to,
|
||||
allowFrom,
|
||||
}),
|
||||
},
|
||||
});
|
||||
whatsappPlugin.config = {
|
||||
...whatsappPlugin.config,
|
||||
|
||||
@@ -4,9 +4,13 @@ import { importFreshModule } from "../../test/helpers/import-fresh.js";
|
||||
|
||||
const execFileMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock("node:child_process", () => ({
|
||||
execFile: (...args: unknown[]) => execFileMock(...args),
|
||||
}));
|
||||
vi.mock("node:child_process", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("node:child_process")>();
|
||||
return {
|
||||
...actual,
|
||||
execFile: (...args: unknown[]) => execFileMock(...args),
|
||||
};
|
||||
});
|
||||
|
||||
const originalVitest = process.env.VITEST;
|
||||
const originalNodeEnv = process.env.NODE_ENV;
|
||||
|
||||
@@ -3,9 +3,13 @@ import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const spawnSyncMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock("node:child_process", () => ({
|
||||
spawnSync: (...args: unknown[]) => spawnSyncMock(...args),
|
||||
}));
|
||||
vi.mock("node:child_process", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("node:child_process")>();
|
||||
return {
|
||||
...actual,
|
||||
spawnSync: (...args: unknown[]) => spawnSyncMock(...args),
|
||||
};
|
||||
});
|
||||
|
||||
import { resolveOsSummary } from "./os-summary.js";
|
||||
|
||||
|
||||
@@ -6,9 +6,13 @@ const spawnMock = vi.hoisted(() => vi.fn());
|
||||
const triggerOpenClawRestartMock = vi.hoisted(() => vi.fn());
|
||||
const scheduleDetachedLaunchdRestartHandoffMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock("node:child_process", () => ({
|
||||
spawn: (...args: unknown[]) => spawnMock(...args),
|
||||
}));
|
||||
vi.mock("node:child_process", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("node:child_process")>();
|
||||
return {
|
||||
...actual,
|
||||
spawn: (...args: unknown[]) => spawnMock(...args),
|
||||
};
|
||||
});
|
||||
vi.mock("./restart.js", () => ({
|
||||
triggerOpenClawRestart: (...args: unknown[]) => triggerOpenClawRestartMock(...args),
|
||||
}));
|
||||
|
||||
@@ -10,10 +10,14 @@ const mockSpawnSync = vi.hoisted(() => vi.fn());
|
||||
const mockResolveGatewayPort = vi.hoisted(() => vi.fn(() => 18789));
|
||||
const mockRestartWarn = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock("node:child_process", () => ({
|
||||
spawnSync: (...args: unknown[]) => mockSpawnSync(...args),
|
||||
execFileSync: vi.fn(),
|
||||
}));
|
||||
vi.mock("node:child_process", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("node:child_process")>();
|
||||
return {
|
||||
...actual,
|
||||
spawnSync: (...args: unknown[]) => mockSpawnSync(...args),
|
||||
execFileSync: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("../config/paths.js", () => ({
|
||||
resolveGatewayPort: () => mockResolveGatewayPort(),
|
||||
|
||||
@@ -16,7 +16,8 @@ function createMockSpawnChild() {
|
||||
return { child, stdout };
|
||||
}
|
||||
|
||||
vi.mock("node:child_process", () => {
|
||||
vi.mock("node:child_process", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("node:child_process")>();
|
||||
const spawn = vi.fn(() => {
|
||||
const { child, stdout } = createMockSpawnChild();
|
||||
process.nextTick(() => {
|
||||
@@ -35,7 +36,10 @@ vi.mock("node:child_process", () => {
|
||||
});
|
||||
return child;
|
||||
});
|
||||
return { spawn };
|
||||
return {
|
||||
...actual,
|
||||
spawn,
|
||||
};
|
||||
});
|
||||
|
||||
const spawnMock = vi.mocked(spawn);
|
||||
|
||||
@@ -7,9 +7,13 @@ import { captureFullEnv } from "../test-utils/env.js";
|
||||
const spawnMock = vi.hoisted(() => vi.fn());
|
||||
const resolvePreferredOpenClawTmpDirMock = vi.hoisted(() => vi.fn(() => os.tmpdir()));
|
||||
|
||||
vi.mock("node:child_process", () => ({
|
||||
spawn: (...args: unknown[]) => spawnMock(...args),
|
||||
}));
|
||||
vi.mock("node:child_process", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("node:child_process")>();
|
||||
return {
|
||||
...actual,
|
||||
spawn: (...args: unknown[]) => spawnMock(...args),
|
||||
};
|
||||
});
|
||||
vi.mock("./tmp-openclaw-dir.js", () => ({
|
||||
resolvePreferredOpenClawTmpDir: () => resolvePreferredOpenClawTmpDirMock(),
|
||||
}));
|
||||
|
||||
@@ -4,15 +4,25 @@ import { captureEnv } from "../test-utils/env.js";
|
||||
const readFileSyncMock = vi.hoisted(() => vi.fn());
|
||||
const readFileMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock("node:fs", () => ({
|
||||
readFileSync: readFileSyncMock,
|
||||
}));
|
||||
vi.mock("node:fs", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("node:fs")>();
|
||||
return {
|
||||
...actual,
|
||||
readFileSync: readFileSyncMock,
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("node:fs/promises", () => ({
|
||||
default: {
|
||||
vi.mock("node:fs/promises", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("node:fs/promises")>();
|
||||
return {
|
||||
...actual,
|
||||
default: {
|
||||
...actual,
|
||||
readFile: readFileMock,
|
||||
},
|
||||
readFile: readFileMock,
|
||||
},
|
||||
}));
|
||||
};
|
||||
});
|
||||
|
||||
let isWSLEnv: typeof import("./wsl.js").isWSLEnv;
|
||||
let isWSLSync: typeof import("./wsl.js").isWSLSync;
|
||||
|
||||
@@ -14,17 +14,33 @@ vi.mock("./facade-runtime.js", () => ({
|
||||
tryLoadActivatedBundledPluginPublicSurfaceModuleSync,
|
||||
}));
|
||||
|
||||
vi.mock("node:fs/promises", () => {
|
||||
const mocked = { mkdir, access, rename };
|
||||
return { ...mocked, default: mocked };
|
||||
vi.mock("node:fs/promises", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("node:fs/promises")>();
|
||||
return {
|
||||
...actual,
|
||||
default: {
|
||||
...actual,
|
||||
mkdir,
|
||||
access,
|
||||
rename,
|
||||
},
|
||||
mkdir,
|
||||
access,
|
||||
rename,
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("node:os", () => ({
|
||||
default: {
|
||||
vi.mock("node:os", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("node:os")>();
|
||||
return {
|
||||
...actual,
|
||||
default: {
|
||||
...actual,
|
||||
homedir: () => "/home/test",
|
||||
},
|
||||
homedir: () => "/home/test",
|
||||
},
|
||||
homedir: () => "/home/test",
|
||||
}));
|
||||
};
|
||||
});
|
||||
|
||||
describe("browser maintenance", () => {
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -294,10 +294,13 @@ describe("command queue", () => {
|
||||
const blocker2 = new Promise<void>((r) => {
|
||||
resolve2 = r;
|
||||
});
|
||||
const firstStarted = createDeferred();
|
||||
|
||||
const first = enqueueCommandInLane(lane, async () => {
|
||||
firstStarted.resolve();
|
||||
await blocker1;
|
||||
});
|
||||
await firstStarted.promise;
|
||||
const drainPromise = waitForActiveTasks(2000);
|
||||
|
||||
// Starts after waitForActiveTasks snapshot and should not block drain completion.
|
||||
|
||||
@@ -4,9 +4,13 @@ const { spawnMock } = vi.hoisted(() => ({
|
||||
spawnMock: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("node:child_process", () => ({
|
||||
spawn: (...args: unknown[]) => spawnMock(...args),
|
||||
}));
|
||||
vi.mock("node:child_process", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("node:child_process")>();
|
||||
return {
|
||||
...actual,
|
||||
spawn: (...args: unknown[]) => spawnMock(...args),
|
||||
};
|
||||
});
|
||||
|
||||
let killProcessTree: typeof import("./kill-tree.js").killProcessTree;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user