mirror of https://github.com/openclaw/openclaw.git
test: speed up core runtime suites
This commit is contained in:
parent
f7285e0a9e
commit
6b6ddcd2a6
|
|
@ -1,4 +1,4 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
|
||||
const hoisted = vi.hoisted(() => {
|
||||
|
|
@ -20,12 +20,14 @@ vi.mock("../../config/sessions.js", () => ({
|
|||
let listAcpSessionEntries: typeof import("./session-meta.js").listAcpSessionEntries;
|
||||
|
||||
describe("listAcpSessionEntries", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
vi.clearAllMocks();
|
||||
beforeAll(async () => {
|
||||
({ listAcpSessionEntries } = await import("./session-meta.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("reads ACP sessions from resolved configured store targets", async () => {
|
||||
const cfg = {
|
||||
session: {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,17 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
sendExecApprovalFollowup: vi.fn(),
|
||||
logWarn: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./bash-tools.exec-approval-followup.js", () => ({
|
||||
sendExecApprovalFollowup: mocks.sendExecApprovalFollowup,
|
||||
}));
|
||||
|
||||
vi.mock("../logger.js", () => ({
|
||||
logWarn: mocks.logWarn,
|
||||
}));
|
||||
|
||||
let sendExecApprovalFollowupResult: typeof import("./bash-tools.exec-host-shared.js").sendExecApprovalFollowupResult;
|
||||
let maxExecApprovalFollowupFailureLogKeys: typeof import("./bash-tools.exec-host-shared.js").MAX_EXEC_APPROVAL_FOLLOWUP_FAILURE_LOG_KEYS;
|
||||
|
|
@ -6,20 +19,16 @@ let sendExecApprovalFollowup: typeof import("./bash-tools.exec-approval-followup
|
|||
let logWarn: typeof import("../logger.js").logWarn;
|
||||
|
||||
describe("sendExecApprovalFollowupResult", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
vi.doMock("./bash-tools.exec-approval-followup.js", () => ({
|
||||
sendExecApprovalFollowup: vi.fn(),
|
||||
}));
|
||||
vi.doMock("../logger.js", () => ({
|
||||
logWarn: vi.fn(),
|
||||
}));
|
||||
beforeAll(async () => {
|
||||
({
|
||||
sendExecApprovalFollowupResult,
|
||||
MAX_EXEC_APPROVAL_FOLLOWUP_FAILURE_LOG_KEYS: maxExecApprovalFollowupFailureLogKeys,
|
||||
} = await import("./bash-tools.exec-host-shared.js"));
|
||||
({ sendExecApprovalFollowup } = await import("./bash-tools.exec-approval-followup.js"));
|
||||
({ logWarn } = await import("../logger.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
vi.mocked(sendExecApprovalFollowup).mockReset();
|
||||
vi.mocked(logWarn).mockReset();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { mergeMockedModule } from "../test-utils/vitest-module-mocks.js";
|
||||
|
||||
const requestHeartbeatNowMock = vi.hoisted(() => vi.fn());
|
||||
|
|
@ -11,7 +11,7 @@ let formatExecFailureReason: typeof import("./bash-tools.exec-runtime.js").forma
|
|||
let resolveExecTarget: typeof import("./bash-tools.exec-runtime.js").resolveExecTarget;
|
||||
|
||||
describe("detectCursorKeyMode", () => {
|
||||
beforeEach(async () => {
|
||||
beforeAll(async () => {
|
||||
({ detectCursorKeyMode } = await import("./bash-tools.exec-runtime.js"));
|
||||
});
|
||||
|
||||
|
|
@ -43,7 +43,7 @@ describe("detectCursorKeyMode", () => {
|
|||
});
|
||||
|
||||
describe("resolveExecTarget", () => {
|
||||
beforeEach(async () => {
|
||||
beforeAll(async () => {
|
||||
({ resolveExecTarget } = await import("./bash-tools.exec-runtime.js"));
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,13 @@
|
|||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
|
||||
import { withFetchPreconnect } from "../test-utils/fetch-mock.js";
|
||||
|
||||
let isMinimaxVlmModel: typeof import("./minimax-vlm.js").isMinimaxVlmModel;
|
||||
let minimaxUnderstandImage: typeof import("./minimax-vlm.js").minimaxUnderstandImage;
|
||||
|
||||
beforeAll(async () => {
|
||||
({ isMinimaxVlmModel, minimaxUnderstandImage } = await import("./minimax-vlm.js"));
|
||||
});
|
||||
|
||||
describe("minimaxUnderstandImage apiKey normalization", () => {
|
||||
const priorFetch = global.fetch;
|
||||
const apiResponse = JSON.stringify({
|
||||
|
|
@ -25,7 +32,6 @@ describe("minimaxUnderstandImage apiKey normalization", () => {
|
|||
});
|
||||
global.fetch = withFetchPreconnect(fetchSpy);
|
||||
|
||||
const { minimaxUnderstandImage } = await import("./minimax-vlm.js");
|
||||
const text = await minimaxUnderstandImage({
|
||||
apiKey,
|
||||
prompt: "hi",
|
||||
|
|
@ -48,8 +54,6 @@ describe("minimaxUnderstandImage apiKey normalization", () => {
|
|||
|
||||
describe("isMinimaxVlmModel", () => {
|
||||
it("only matches the canonical MiniMax VLM model id", async () => {
|
||||
const { isMinimaxVlmModel } = await import("./minimax-vlm.js");
|
||||
|
||||
expect(isMinimaxVlmModel("minimax", "MiniMax-VL-01")).toBe(true);
|
||||
expect(isMinimaxVlmModel("minimax-portal", "MiniMax-VL-01")).toBe(true);
|
||||
expect(isMinimaxVlmModel("minimax-portal", "custom-vision")).toBe(false);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,15 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { beforeAll, describe, expect, it } from "vitest";
|
||||
|
||||
let normalizeProviderSpecificConfig: typeof import("./models-config.providers.policy.js").normalizeProviderSpecificConfig;
|
||||
let resolveProviderConfigApiKeyResolver: typeof import("./models-config.providers.policy.js").resolveProviderConfigApiKeyResolver;
|
||||
|
||||
beforeAll(async () => {
|
||||
({ normalizeProviderSpecificConfig, resolveProviderConfigApiKeyResolver } =
|
||||
await import("./models-config.providers.policy.js"));
|
||||
});
|
||||
|
||||
describe("models-config.providers.policy", () => {
|
||||
it("resolves config apiKey markers through provider plugin hooks", async () => {
|
||||
const { resolveProviderConfigApiKeyResolver } =
|
||||
await import("./models-config.providers.policy.js");
|
||||
const env = {
|
||||
AWS_PROFILE: "default",
|
||||
} as NodeJS.ProcessEnv;
|
||||
|
|
@ -14,8 +20,6 @@ describe("models-config.providers.policy", () => {
|
|||
});
|
||||
|
||||
it("resolves anthropic-vertex ADC markers through provider plugin hooks", async () => {
|
||||
const { resolveProviderConfigApiKeyResolver } =
|
||||
await import("./models-config.providers.policy.js");
|
||||
const resolver = resolveProviderConfigApiKeyResolver("anthropic-vertex");
|
||||
|
||||
expect(resolver).toBeTypeOf("function");
|
||||
|
|
@ -27,8 +31,6 @@ describe("models-config.providers.policy", () => {
|
|||
});
|
||||
|
||||
it("normalizes Google provider config through provider plugin hooks", async () => {
|
||||
const { normalizeProviderSpecificConfig } = await import("./models-config.providers.policy.js");
|
||||
|
||||
expect(
|
||||
normalizeProviderSpecificConfig("google", {
|
||||
api: "google-generative-ai",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import type { RuntimeWebFetchFirecrawlMetadata } from "../secrets/runtime-web-tools.types.js";
|
||||
import type { RuntimeWebSearchMetadata } from "../secrets/runtime-web-tools.types.js";
|
||||
|
|
@ -138,8 +138,7 @@ async function prepareAndActivate(params: { config: OpenClawConfig; env?: NodeJS
|
|||
describe("openclaw tools runtime web metadata wiring", () => {
|
||||
const priorFetch = global.fetch;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
beforeAll(async () => {
|
||||
secretsRuntime = await import("../secrets/runtime.js");
|
||||
({ createWebFetchTool, createWebSearchTool } = await import("./tools/web-tools.js"));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -12,6 +12,11 @@ import {
|
|||
import { subscribeEmbeddedPiSession } from "./pi-embedded-subscribe.js";
|
||||
|
||||
describe("subscribeEmbeddedPiSession", () => {
|
||||
async function flushBlockReplyCallbacks(): Promise<void> {
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
}
|
||||
|
||||
function createAgentEventHarness(options?: { runId?: string; sessionKey?: string }) {
|
||||
const { session, emit } = createStubSessionHarness();
|
||||
const onAgentEvent = vi.fn();
|
||||
|
|
@ -132,10 +137,9 @@ describe("subscribeEmbeddedPiSession", () => {
|
|||
} as AssistantMessage;
|
||||
|
||||
emit({ type: "message_end", message: assistantMessage });
|
||||
await flushBlockReplyCallbacks();
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(onBlockReply).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
expect(onBlockReply).toHaveBeenCalledTimes(1);
|
||||
expect(onBlockReply.mock.calls[0][0].text).toBe("Final answer");
|
||||
|
||||
const streamTexts = onReasoningStream.mock.calls
|
||||
|
|
@ -176,10 +180,9 @@ describe("subscribeEmbeddedPiSession", () => {
|
|||
message: { role: "assistant" },
|
||||
assistantMessageEvent: { type: "text_end" },
|
||||
});
|
||||
await flushBlockReplyCallbacks();
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(onBlockReply.mock.calls.length).toBeGreaterThan(0);
|
||||
});
|
||||
expect(onBlockReply.mock.calls.length).toBeGreaterThan(0);
|
||||
const payloadTexts = onBlockReply.mock.calls
|
||||
.map((call) => call[0]?.text)
|
||||
.filter((value): value is string => typeof value === "string");
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const resolveProviderCapabilitiesWithPluginMock = vi.fn((params: { provider: string }) => {
|
||||
switch (params.provider) {
|
||||
|
|
@ -65,24 +65,22 @@ let shouldSanitizeGeminiThoughtSignaturesForModel: typeof import("./provider-cap
|
|||
let supportsOpenAiCompatTurnValidation: typeof import("./provider-capabilities.js").supportsOpenAiCompatTurnValidation;
|
||||
let usesMoonshotThinkingPayloadCompat: typeof import("./provider-capabilities.js").usesMoonshotThinkingPayloadCompat;
|
||||
|
||||
async function loadFreshProviderCapabilitiesModuleForTest() {
|
||||
vi.resetModules();
|
||||
({
|
||||
isAnthropicProviderFamily,
|
||||
isOpenAiProviderFamily,
|
||||
requiresOpenAiCompatibleAnthropicToolPayload,
|
||||
resolveProviderCapabilities,
|
||||
resolveTranscriptToolCallIdMode,
|
||||
shouldDropThinkingBlocksForModel,
|
||||
shouldSanitizeGeminiThoughtSignaturesForModel,
|
||||
supportsOpenAiCompatTurnValidation,
|
||||
usesMoonshotThinkingPayloadCompat,
|
||||
} = await import("./provider-capabilities.js"));
|
||||
}
|
||||
|
||||
describe("resolveProviderCapabilities", () => {
|
||||
beforeEach(async () => {
|
||||
await loadFreshProviderCapabilitiesModuleForTest();
|
||||
beforeAll(async () => {
|
||||
({
|
||||
isAnthropicProviderFamily,
|
||||
isOpenAiProviderFamily,
|
||||
requiresOpenAiCompatibleAnthropicToolPayload,
|
||||
resolveProviderCapabilities,
|
||||
resolveTranscriptToolCallIdMode,
|
||||
shouldDropThinkingBlocksForModel,
|
||||
shouldSanitizeGeminiThoughtSignaturesForModel,
|
||||
supportsOpenAiCompatTurnValidation,
|
||||
usesMoonshotThinkingPayloadCompat,
|
||||
} = await import("./provider-capabilities.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
resolveProviderCapabilitiesWithPluginMock.mockClear();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const hoisted = vi.hoisted(() => ({
|
||||
resolveRuntimePluginRegistry: vi.fn(),
|
||||
|
|
@ -9,14 +9,18 @@ vi.mock("../plugins/loader.js", () => ({
|
|||
}));
|
||||
|
||||
describe("ensureRuntimePluginsLoaded", () => {
|
||||
let ensureRuntimePluginsLoaded: typeof import("./runtime-plugins.js").ensureRuntimePluginsLoaded;
|
||||
|
||||
beforeAll(async () => {
|
||||
({ ensureRuntimePluginsLoaded } = await import("./runtime-plugins.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
hoisted.resolveRuntimePluginRegistry.mockReset();
|
||||
hoisted.resolveRuntimePluginRegistry.mockReturnValue(undefined);
|
||||
vi.resetModules();
|
||||
});
|
||||
|
||||
it("does not reactivate plugins when a process already has an active registry", async () => {
|
||||
const { ensureRuntimePluginsLoaded } = await import("./runtime-plugins.js");
|
||||
hoisted.resolveRuntimePluginRegistry.mockReturnValue({});
|
||||
|
||||
ensureRuntimePluginsLoaded({
|
||||
|
|
@ -29,8 +33,6 @@ describe("ensureRuntimePluginsLoaded", () => {
|
|||
});
|
||||
|
||||
it("resolves runtime plugins through the shared runtime helper", async () => {
|
||||
const { ensureRuntimePluginsLoaded } = await import("./runtime-plugins.js");
|
||||
|
||||
ensureRuntimePluginsLoaded({
|
||||
config: {} as never,
|
||||
workspaceDir: "/tmp/workspace",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
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 { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const FAKE_STARTTIME = 12345;
|
||||
let __testing: typeof import("./session-write-lock.js").__testing;
|
||||
|
|
@ -10,25 +10,14 @@ let cleanStaleLockFiles: typeof import("./session-write-lock.js").cleanStaleLock
|
|||
let resetSessionWriteLockStateForTest: typeof import("./session-write-lock.js").resetSessionWriteLockStateForTest;
|
||||
let resolveSessionLockMaxHoldFromTimeout: typeof import("./session-write-lock.js").resolveSessionLockMaxHoldFromTimeout;
|
||||
|
||||
async function loadFreshSessionWriteLockModuleForTest() {
|
||||
vi.resetModules();
|
||||
// Mock getProcessStartTime so PID-recycling detection works on non-Linux
|
||||
// (macOS, CI runners). isPidAlive is left unmocked.
|
||||
vi.doMock("../shared/pid-alive.js", async (importOriginal) => {
|
||||
const original = await importOriginal<typeof import("../shared/pid-alive.js")>();
|
||||
return {
|
||||
...original,
|
||||
getProcessStartTime: (pid: number) => (pid === process.pid ? FAKE_STARTTIME : null),
|
||||
};
|
||||
});
|
||||
({
|
||||
__testing,
|
||||
acquireSessionWriteLock,
|
||||
cleanStaleLockFiles,
|
||||
resetSessionWriteLockStateForTest,
|
||||
resolveSessionLockMaxHoldFromTimeout,
|
||||
} = await import("./session-write-lock.js"));
|
||||
}
|
||||
vi.mock("../shared/pid-alive.js", async (importOriginal) => {
|
||||
const original = await importOriginal<typeof import("../shared/pid-alive.js")>();
|
||||
return {
|
||||
...original,
|
||||
// Keep liveness checks real; only pin process start time for PID recycle coverage.
|
||||
getProcessStartTime: (pid: number) => (pid === process.pid ? FAKE_STARTTIME : null),
|
||||
};
|
||||
});
|
||||
|
||||
async function expectLockRemovedOnlyAfterFinalRelease(params: {
|
||||
lockPath: string;
|
||||
|
|
@ -104,8 +93,14 @@ async function expectActiveInProcessLockIsNotReclaimed(params?: {
|
|||
}
|
||||
|
||||
describe("acquireSessionWriteLock", () => {
|
||||
beforeEach(async () => {
|
||||
await loadFreshSessionWriteLockModuleForTest();
|
||||
beforeAll(async () => {
|
||||
({
|
||||
__testing,
|
||||
acquireSessionWriteLock,
|
||||
cleanStaleLockFiles,
|
||||
resetSessionWriteLockStateForTest,
|
||||
resolveSessionLockMaxHoldFromTimeout,
|
||||
} = await import("./session-write-lock.js"));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
|
|
|||
|
|
@ -1,33 +1,36 @@
|
|||
import type { Model } from "@mariozechner/pi-ai";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
|
||||
const createAnthropicVertexStreamFnForModel = vi.fn();
|
||||
const ensureCustomApiRegistered = vi.fn();
|
||||
const resolveProviderStreamFn = vi.fn();
|
||||
|
||||
vi.mock("./anthropic-vertex-stream.js", () => ({
|
||||
createAnthropicVertexStreamFnForModel,
|
||||
}));
|
||||
|
||||
vi.mock("./custom-api-registry.js", () => ({
|
||||
ensureCustomApiRegistered,
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/provider-runtime.js", () => ({
|
||||
resolveProviderStreamFn,
|
||||
}));
|
||||
|
||||
let prepareModelForSimpleCompletion: typeof import("./simple-completion-transport.js").prepareModelForSimpleCompletion;
|
||||
|
||||
describe("prepareModelForSimpleCompletion", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
beforeAll(async () => {
|
||||
({ prepareModelForSimpleCompletion } = await import("./simple-completion-transport.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
createAnthropicVertexStreamFnForModel.mockReset();
|
||||
ensureCustomApiRegistered.mockReset();
|
||||
resolveProviderStreamFn.mockReset();
|
||||
createAnthropicVertexStreamFnForModel.mockReturnValue("vertex-stream");
|
||||
resolveProviderStreamFn.mockReturnValue("ollama-stream");
|
||||
|
||||
vi.doMock("./anthropic-vertex-stream.js", () => ({
|
||||
createAnthropicVertexStreamFnForModel,
|
||||
}));
|
||||
vi.doMock("./custom-api-registry.js", () => ({
|
||||
ensureCustomApiRegistered,
|
||||
}));
|
||||
vi.doMock("../plugins/provider-runtime.js", () => ({
|
||||
resolveProviderStreamFn,
|
||||
}));
|
||||
|
||||
({ prepareModelForSimpleCompletion } = await import("./simple-completion-transport.js"));
|
||||
});
|
||||
|
||||
it("registers the configured Ollama transport and keeps the original api", () => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import type { PluginManifestRegistry } from "../../plugins/manifest-registry.js";
|
||||
import { createTrackedTempDirs } from "../../test-utils/tracked-temp-dirs.js";
|
||||
|
|
@ -15,14 +15,6 @@ vi.mock("../../plugins/manifest-registry.js", () => ({
|
|||
|
||||
let resolvePluginSkillDirs: typeof import("./plugin-skills.js").resolvePluginSkillDirs;
|
||||
|
||||
async function loadFreshPluginSkillsModuleForTest() {
|
||||
vi.resetModules();
|
||||
vi.doMock("../../plugins/manifest-registry.js", () => ({
|
||||
loadPluginManifestRegistry: (...args: unknown[]) => hoisted.loadPluginManifestRegistry(...args),
|
||||
}));
|
||||
({ resolvePluginSkillDirs } = await import("./plugin-skills.js"));
|
||||
}
|
||||
|
||||
const tempDirs = createTrackedTempDirs();
|
||||
|
||||
function buildRegistry(params: { acpxRoot: string; helperRoot: string }): PluginManifestRegistry {
|
||||
|
|
@ -109,8 +101,12 @@ afterEach(async () => {
|
|||
});
|
||||
|
||||
describe("resolvePluginSkillDirs", () => {
|
||||
beforeEach(async () => {
|
||||
await loadFreshPluginSkillsModuleForTest();
|
||||
beforeAll(async () => {
|
||||
({ resolvePluginSkillDirs } = await import("./plugin-skills.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
hoisted.loadPluginManifestRegistry.mockReset();
|
||||
});
|
||||
|
||||
it.each([
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const watchMock = vi.fn(() => ({
|
||||
on: vi.fn(),
|
||||
|
|
@ -9,18 +9,17 @@ const watchMock = vi.fn(() => ({
|
|||
|
||||
let refreshModule: typeof import("./refresh.js");
|
||||
|
||||
async function loadFreshRefreshModuleForTest() {
|
||||
vi.resetModules();
|
||||
vi.doMock("chokidar", () => ({
|
||||
default: { watch: watchMock },
|
||||
}));
|
||||
refreshModule = await import("./refresh.js");
|
||||
}
|
||||
vi.mock("chokidar", () => ({
|
||||
default: { watch: watchMock },
|
||||
}));
|
||||
|
||||
describe("ensureSkillsWatcher", () => {
|
||||
beforeEach(async () => {
|
||||
beforeAll(async () => {
|
||||
refreshModule = await import("./refresh.js");
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
watchMock.mockClear();
|
||||
await loadFreshRefreshModuleForTest();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { afterAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const callGatewayMock = vi.fn();
|
||||
const configState = vi.hoisted(() => ({
|
||||
|
|
@ -15,28 +15,19 @@ vi.mock("../../gateway/call.js", () => ({
|
|||
let callGatewayTool: typeof import("./gateway.js").callGatewayTool;
|
||||
let resolveGatewayOptions: typeof import("./gateway.js").resolveGatewayOptions;
|
||||
|
||||
async function loadFreshGatewayToolModuleForTest() {
|
||||
vi.resetModules();
|
||||
vi.doMock("../../config/config.js", () => ({
|
||||
loadConfig: () => configState.value,
|
||||
resolveGatewayPort: () => 18789,
|
||||
}));
|
||||
vi.doMock("../../gateway/call.js", () => ({
|
||||
callGateway: (...args: unknown[]) => callGatewayMock(...args),
|
||||
}));
|
||||
({ callGatewayTool, resolveGatewayOptions } = await import("./gateway.js"));
|
||||
}
|
||||
|
||||
describe("gateway tool defaults", () => {
|
||||
const envSnapshot = {
|
||||
openclaw: process.env.OPENCLAW_GATEWAY_TOKEN,
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeAll(async () => {
|
||||
({ callGatewayTool, resolveGatewayOptions } = await import("./gateway.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
callGatewayMock.mockClear();
|
||||
configState.value = {};
|
||||
delete process.env.OPENCLAW_GATEWAY_TOKEN;
|
||||
await loadFreshGatewayToolModuleForTest();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const gatewayMocks = vi.hoisted(() => ({
|
||||
callGatewayTool: vi.fn(),
|
||||
|
|
@ -21,12 +21,14 @@ function node({ nodeId, ...overrides }: Partial<NodeListNode> & { nodeId: string
|
|||
};
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
gatewayMocks.callGatewayTool.mockReset();
|
||||
beforeAll(async () => {
|
||||
({ listNodes, resolveNodeIdFromList } = await import("./nodes-utils.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
gatewayMocks.callGatewayTool.mockReset();
|
||||
});
|
||||
|
||||
describe("resolveNodeIdFromList defaults", () => {
|
||||
it("falls back to most recently connected node when multiple non-Mac candidates exist", () => {
|
||||
const nodes: NodeListNode[] = [
|
||||
|
|
|
|||
|
|
@ -1,12 +1,59 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
gatewayCall: vi.fn(),
|
||||
createAgentToAgentPolicy: vi.fn(() => ({})),
|
||||
createSessionVisibilityGuard: vi.fn(async () => ({
|
||||
check: () => ({ allowed: true }),
|
||||
})),
|
||||
resolveEffectiveSessionToolsVisibility: vi.fn(() => "all"),
|
||||
resolveSandboxedSessionToolContext: vi.fn(() => ({
|
||||
mainKey: "main",
|
||||
alias: "main",
|
||||
requesterInternalKey: undefined,
|
||||
restrictToSpawned: false,
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock("../../gateway/call.js", () => ({
|
||||
callGateway: (opts: unknown) => mocks.gatewayCall(opts),
|
||||
}));
|
||||
|
||||
vi.mock("./sessions-helpers.js", async (importActual) => {
|
||||
const actual = await importActual<typeof import("./sessions-helpers.js")>();
|
||||
return {
|
||||
...actual,
|
||||
createAgentToAgentPolicy: () => mocks.createAgentToAgentPolicy(),
|
||||
createSessionVisibilityGuard: async () => await mocks.createSessionVisibilityGuard(),
|
||||
resolveEffectiveSessionToolsVisibility: () => mocks.resolveEffectiveSessionToolsVisibility(),
|
||||
resolveSandboxedSessionToolContext: () => mocks.resolveSandboxedSessionToolContext(),
|
||||
};
|
||||
});
|
||||
|
||||
describe("sessions-list-tool", () => {
|
||||
let createSessionsListTool: typeof import("./sessions-list-tool.js").createSessionsListTool;
|
||||
|
||||
beforeAll(async () => {
|
||||
({ createSessionsListTool } = await import("./sessions-list-tool.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
vi.clearAllMocks();
|
||||
mocks.createAgentToAgentPolicy.mockReturnValue({});
|
||||
mocks.createSessionVisibilityGuard.mockResolvedValue({
|
||||
check: () => ({ allowed: true }),
|
||||
});
|
||||
mocks.resolveEffectiveSessionToolsVisibility.mockReturnValue("all");
|
||||
mocks.resolveSandboxedSessionToolContext.mockReturnValue({
|
||||
mainKey: "main",
|
||||
alias: "main",
|
||||
requesterInternalKey: undefined,
|
||||
restrictToSpawned: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps deliveryContext.threadId in sessions_list results", async () => {
|
||||
const gatewayCallMock = vi.fn(async (opts: unknown) => {
|
||||
mocks.gatewayCall.mockImplementation(async (opts: unknown) => {
|
||||
const request = opts as { method?: string };
|
||||
if (request.method === "sessions.list") {
|
||||
return {
|
||||
|
|
@ -39,30 +86,6 @@ describe("sessions-list-tool", () => {
|
|||
}
|
||||
return {};
|
||||
});
|
||||
|
||||
vi.doMock("../../gateway/call.js", () => ({
|
||||
callGateway: gatewayCallMock,
|
||||
}));
|
||||
vi.doMock("./sessions-helpers.js", async () => {
|
||||
const actual =
|
||||
await vi.importActual<typeof import("./sessions-helpers.js")>("./sessions-helpers.js");
|
||||
return {
|
||||
...actual,
|
||||
createAgentToAgentPolicy: () => ({}),
|
||||
createSessionVisibilityGuard: async () => ({
|
||||
check: () => ({ allowed: true }),
|
||||
}),
|
||||
resolveEffectiveSessionToolsVisibility: () => "all",
|
||||
resolveSandboxedSessionToolContext: () => ({
|
||||
mainKey: "main",
|
||||
alias: "main",
|
||||
requesterInternalKey: undefined,
|
||||
restrictToSpawned: false,
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
const { createSessionsListTool } = await import("./sessions-list-tool.js");
|
||||
const tool = createSessionsListTool({ config: {} as never });
|
||||
|
||||
const result = await tool.execute("call-1", {});
|
||||
|
|
@ -92,7 +115,7 @@ describe("sessions-list-tool", () => {
|
|||
});
|
||||
|
||||
it("keeps numeric deliveryContext.threadId in sessions_list results", async () => {
|
||||
const gatewayCallMock = vi.fn(async (opts: unknown) => {
|
||||
mocks.gatewayCall.mockImplementation(async (opts: unknown) => {
|
||||
const request = opts as { method?: string };
|
||||
if (request.method === "sessions.list") {
|
||||
return {
|
||||
|
|
@ -114,30 +137,6 @@ describe("sessions-list-tool", () => {
|
|||
}
|
||||
return {};
|
||||
});
|
||||
|
||||
vi.doMock("../../gateway/call.js", () => ({
|
||||
callGateway: gatewayCallMock,
|
||||
}));
|
||||
vi.doMock("./sessions-helpers.js", async () => {
|
||||
const actual =
|
||||
await vi.importActual<typeof import("./sessions-helpers.js")>("./sessions-helpers.js");
|
||||
return {
|
||||
...actual,
|
||||
createAgentToAgentPolicy: () => ({}),
|
||||
createSessionVisibilityGuard: async () => ({
|
||||
check: () => ({ allowed: true }),
|
||||
}),
|
||||
resolveEffectiveSessionToolsVisibility: () => "all",
|
||||
resolveSandboxedSessionToolContext: () => ({
|
||||
mainKey: "main",
|
||||
alias: "main",
|
||||
requesterInternalKey: undefined,
|
||||
restrictToSpawned: false,
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
const { createSessionsListTool } = await import("./sessions-list-tool.js");
|
||||
const tool = createSessionsListTool({ config: {} as never });
|
||||
|
||||
const result = await tool.execute("call-2", {});
|
||||
|
|
@ -161,7 +160,7 @@ describe("sessions-list-tool", () => {
|
|||
});
|
||||
|
||||
it("keeps live session setting metadata in sessions_list results", async () => {
|
||||
const gatewayCallMock = vi.fn(async (opts: unknown) => {
|
||||
mocks.gatewayCall.mockImplementation(async (opts: unknown) => {
|
||||
const request = opts as { method?: string };
|
||||
if (request.method === "sessions.list") {
|
||||
return {
|
||||
|
|
@ -183,30 +182,6 @@ describe("sessions-list-tool", () => {
|
|||
}
|
||||
return {};
|
||||
});
|
||||
|
||||
vi.doMock("../../gateway/call.js", () => ({
|
||||
callGateway: gatewayCallMock,
|
||||
}));
|
||||
vi.doMock("./sessions-helpers.js", async () => {
|
||||
const actual =
|
||||
await vi.importActual<typeof import("./sessions-helpers.js")>("./sessions-helpers.js");
|
||||
return {
|
||||
...actual,
|
||||
createAgentToAgentPolicy: () => ({}),
|
||||
createSessionVisibilityGuard: async () => ({
|
||||
check: () => ({ allowed: true }),
|
||||
}),
|
||||
resolveEffectiveSessionToolsVisibility: () => "all",
|
||||
resolveSandboxedSessionToolContext: () => ({
|
||||
mainKey: "main",
|
||||
alias: "main",
|
||||
requesterInternalKey: undefined,
|
||||
restrictToSpawned: false,
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
const { createSessionsListTool } = await import("./sessions-list-tool.js");
|
||||
const tool = createSessionsListTool({ config: {} as never });
|
||||
|
||||
const result = await tool.execute("call-3", {});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
const callGatewayMock = vi.fn();
|
||||
vi.mock("../../gateway/call.js", () => ({
|
||||
|
|
@ -14,11 +14,7 @@ let resolveSessionReference: typeof import("./sessions-resolution.js").resolveSe
|
|||
let shouldVerifyRequesterSpawnedSessionVisibility: typeof import("./sessions-resolution.js").shouldVerifyRequesterSpawnedSessionVisibility;
|
||||
let shouldResolveSessionIdInput: typeof import("./sessions-resolution.js").shouldResolveSessionIdInput;
|
||||
|
||||
async function loadFreshSessionsResolutionModuleForTest() {
|
||||
vi.resetModules();
|
||||
vi.doMock("../../gateway/call.js", () => ({
|
||||
callGateway: (opts: unknown) => callGatewayMock(opts),
|
||||
}));
|
||||
beforeAll(async () => {
|
||||
({
|
||||
isResolvedSessionVisibleToRequester,
|
||||
looksLikeSessionId,
|
||||
|
|
@ -30,11 +26,10 @@ async function loadFreshSessionsResolutionModuleForTest() {
|
|||
shouldVerifyRequesterSpawnedSessionVisibility,
|
||||
shouldResolveSessionIdInput,
|
||||
} = await import("./sessions-resolution.js"));
|
||||
}
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeEach(() => {
|
||||
callGatewayMock.mockReset();
|
||||
await loadFreshSessionsResolutionModuleForTest();
|
||||
});
|
||||
|
||||
describe("resolveMainSessionAlias", () => {
|
||||
|
|
|
|||
|
|
@ -1,18 +1,17 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { SILENT_REPLY_TOKEN } from "../../auto-reply/tokens.js";
|
||||
import * as ttsRuntime from "../../tts/tts.js";
|
||||
import { createTtsTool } from "./tts-tool.js";
|
||||
|
||||
let textToSpeechSpy: ReturnType<typeof vi.spyOn>;
|
||||
|
||||
describe("createTtsTool", () => {
|
||||
beforeEach(async () => {
|
||||
beforeEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
vi.resetModules();
|
||||
const ttsRuntime = await import("../../tts/tts.js");
|
||||
textToSpeechSpy = vi.spyOn(ttsRuntime, "textToSpeech");
|
||||
});
|
||||
|
||||
it("uses SILENT_REPLY_TOKEN in guidance text", async () => {
|
||||
const { createTtsTool } = await import("./tts-tool.js");
|
||||
it("uses SILENT_REPLY_TOKEN in guidance text", () => {
|
||||
const tool = createTtsTool();
|
||||
|
||||
expect(tool.description).toContain(SILENT_REPLY_TOKEN);
|
||||
|
|
@ -26,7 +25,6 @@ describe("createTtsTool", () => {
|
|||
voiceCompatible: true,
|
||||
});
|
||||
|
||||
const { createTtsTool } = await import("./tts-tool.js");
|
||||
const tool = createTtsTool();
|
||||
const result = await tool.execute("call-1", { text: "hello" });
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
vi.mock("../plugins/provider-runtime.js", () => ({
|
||||
resolveProviderCapabilitiesWithPlugin: vi.fn(({ provider }: { provider?: string }) => {
|
||||
|
|
@ -29,14 +29,13 @@ vi.mock("../plugins/provider-runtime.js", () => ({
|
|||
|
||||
let resolveTranscriptPolicy: typeof import("./transcript-policy.js").resolveTranscriptPolicy;
|
||||
|
||||
async function loadFreshTranscriptPolicyModuleForTest() {
|
||||
vi.resetModules();
|
||||
({ resolveTranscriptPolicy } = await import("./transcript-policy.js"));
|
||||
}
|
||||
|
||||
describe("resolveTranscriptPolicy", () => {
|
||||
beforeEach(async () => {
|
||||
await loadFreshTranscriptPolicyModuleForTest();
|
||||
beforeAll(async () => {
|
||||
({ resolveTranscriptPolicy } = await import("./transcript-policy.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("enables sanitizeToolCallIds for Anthropic provider", () => {
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ describe("emitResetCommandHooks", () => {
|
|||
workspaceDir: "/tmp/openclaw-workspace",
|
||||
});
|
||||
|
||||
await vi.waitFor(() => expect(hookRunnerMocks.runBeforeReset).toHaveBeenCalledTimes(1));
|
||||
expect(hookRunnerMocks.runBeforeReset).toHaveBeenCalledTimes(1);
|
||||
const [, ctx] = hookRunnerMocks.runBeforeReset.mock.calls[0] ?? [];
|
||||
return ctx;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ describe("session hook context wiring", () => {
|
|||
commandAuthorized: true,
|
||||
});
|
||||
|
||||
await vi.waitFor(() => expect(hookRunnerMocks.runSessionStart).toHaveBeenCalledTimes(1));
|
||||
expect(hookRunnerMocks.runSessionStart).toHaveBeenCalledTimes(1);
|
||||
const [event, context] = hookRunnerMocks.runSessionStart.mock.calls[0] ?? [];
|
||||
expect(event).toMatchObject({ sessionKey });
|
||||
expect(context).toMatchObject({ sessionKey, agentId: "main" });
|
||||
|
|
@ -89,8 +89,8 @@ describe("session hook context wiring", () => {
|
|||
commandAuthorized: true,
|
||||
});
|
||||
|
||||
await vi.waitFor(() => expect(hookRunnerMocks.runSessionEnd).toHaveBeenCalledTimes(1));
|
||||
await vi.waitFor(() => expect(hookRunnerMocks.runSessionStart).toHaveBeenCalledTimes(1));
|
||||
expect(hookRunnerMocks.runSessionEnd).toHaveBeenCalledTimes(1);
|
||||
expect(hookRunnerMocks.runSessionStart).toHaveBeenCalledTimes(1);
|
||||
const [event, context] = hookRunnerMocks.runSessionEnd.mock.calls[0] ?? [];
|
||||
expect(event).toMatchObject({ sessionKey });
|
||||
expect(context).toMatchObject({ sessionKey, agentId: "main" });
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { MsgContext } from "../auto-reply/templating.js";
|
||||
|
||||
const recordSessionMetaFromInboundMock = vi.fn((_args?: unknown) => Promise.resolve(undefined));
|
||||
|
|
@ -21,9 +21,11 @@ describe("recordInboundSession", () => {
|
|||
OriginatingTo: "demo-channel:1234",
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
beforeAll(async () => {
|
||||
({ recordInboundSession } = await import("./session.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
recordSessionMetaFromInboundMock.mockClear();
|
||||
updateLastRouteMock.mockClear();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { buildWebSearchProviderConfig } from "./test-helpers.js";
|
||||
|
||||
vi.mock("../runtime.js", () => ({
|
||||
|
|
@ -77,8 +77,7 @@ vi.mock("../plugins/web-search-providers.js", () => {
|
|||
let validateConfigObjectWithPlugins: typeof import("./config.js").validateConfigObjectWithPlugins;
|
||||
let resolveSearchProvider: typeof import("../agents/tools/web-search.js").__testing.resolveSearchProvider;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
beforeAll(async () => {
|
||||
({ validateConfigObjectWithPlugins } = await import("./config.js"));
|
||||
({
|
||||
__testing: { resolveSearchProvider },
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
createConfigIO: vi.fn().mockReturnValue({
|
||||
|
|
@ -13,11 +13,14 @@ vi.mock("./io.js", () => ({
|
|||
let formatConfigPath: typeof import("./logging.js").formatConfigPath;
|
||||
let logConfigUpdated: typeof import("./logging.js").logConfigUpdated;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
beforeAll(async () => {
|
||||
({ formatConfigPath, logConfigUpdated } = await import("./logging.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
mocks.createConfigIO.mockClear();
|
||||
});
|
||||
|
||||
describe("config logging", () => {
|
||||
it("formats the live config path when no explicit path is provided", () => {
|
||||
expect(formatConfigPath()).toBe("/tmp/openclaw-dev/openclaw.json");
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { ConfigFileSnapshot, OpenClawConfig } from "./types.js";
|
||||
|
||||
const mockLoadConfig = vi.hoisted(() => vi.fn<() => OpenClawConfig>());
|
||||
const mockReadConfigFileSnapshot = vi.hoisted(() => vi.fn<() => Promise<ConfigFileSnapshot>>());
|
||||
const mockLoadPluginManifestRegistry = vi.hoisted(() => vi.fn());
|
||||
|
||||
let readBestEffortRuntimeConfigSchema: typeof import("./runtime-schema.js").readBestEffortRuntimeConfigSchema;
|
||||
let loadGatewayRuntimeConfigSchema: typeof import("./runtime-schema.js").loadGatewayRuntimeConfigSchema;
|
||||
|
||||
vi.mock("./config.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("./config.js")>();
|
||||
return {
|
||||
|
|
@ -127,7 +130,6 @@ function makeManifestRegistry() {
|
|||
}
|
||||
|
||||
async function readSchemaNodes() {
|
||||
const { readBestEffortRuntimeConfigSchema } = await import("./runtime-schema.js");
|
||||
const result = await readBestEffortRuntimeConfigSchema();
|
||||
const schema = result.schema as { properties?: Record<string, unknown> };
|
||||
const channelsNode = schema.properties?.channels as Record<string, unknown> | undefined;
|
||||
|
|
@ -139,6 +141,11 @@ async function readSchemaNodes() {
|
|||
return { channelProps, entryProps };
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
({ readBestEffortRuntimeConfigSchema, loadGatewayRuntimeConfigSchema } =
|
||||
await import("./runtime-schema.js"));
|
||||
});
|
||||
|
||||
describe("readBestEffortRuntimeConfigSchema", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
|
@ -192,7 +199,6 @@ describe("loadGatewayRuntimeConfigSchema", () => {
|
|||
});
|
||||
|
||||
it("uses manifest metadata instead of booting plugin runtime", async () => {
|
||||
const { loadGatewayRuntimeConfigSchema } = await import("./runtime-schema.js");
|
||||
const result = loadGatewayRuntimeConfigSchema();
|
||||
const schema = result.schema as { properties?: Record<string, unknown> };
|
||||
const channelsNode = schema.properties?.channels as Record<string, unknown> | undefined;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { SessionEntry } from "./types.js";
|
||||
|
||||
const storeState = vi.hoisted(() => ({
|
||||
|
|
@ -26,12 +26,14 @@ const buildEntry = (deliveryContext: SessionEntry["deliveryContext"]): SessionEn
|
|||
deliveryContext,
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
storeState.store = {};
|
||||
beforeAll(async () => {
|
||||
({ extractDeliveryInfo, parseSessionThreadInfo } = await import("./delivery-info.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
storeState.store = {};
|
||||
});
|
||||
|
||||
describe("extractDeliveryInfo", () => {
|
||||
it("parses base session and thread/topic ids", () => {
|
||||
expect(parseSessionThreadInfo("agent:main:telegram:group:1:topic:55")).toEqual({
|
||||
|
|
|
|||
|
|
@ -1,11 +1,19 @@
|
|||
import { describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const mockLoadPluginManifestRegistry = vi.hoisted(() => vi.fn());
|
||||
|
||||
let validateConfigObjectWithPlugins: typeof import("./validation.js").validateConfigObjectWithPlugins;
|
||||
let validateConfigObjectRawWithPlugins: typeof import("./validation.js").validateConfigObjectRawWithPlugins;
|
||||
|
||||
vi.mock("../plugins/manifest-registry.js", () => ({
|
||||
loadPluginManifestRegistry: (...args: unknown[]) => mockLoadPluginManifestRegistry(...args),
|
||||
}));
|
||||
|
||||
beforeAll(async () => {
|
||||
({ validateConfigObjectWithPlugins, validateConfigObjectRawWithPlugins } =
|
||||
await import("./validation.js"));
|
||||
});
|
||||
|
||||
function setupTelegramSchemaWithDefault() {
|
||||
mockLoadPluginManifestRegistry.mockReturnValue({
|
||||
diagnostics: [],
|
||||
|
|
@ -44,7 +52,6 @@ describe("validateConfigObjectWithPlugins channel metadata (applyDefaults: true)
|
|||
it("applies bundled channel defaults from plugin-owned schema metadata", async () => {
|
||||
setupTelegramSchemaWithDefault();
|
||||
|
||||
const { validateConfigObjectWithPlugins } = await import("./validation.js");
|
||||
const result = validateConfigObjectWithPlugins({
|
||||
channels: {
|
||||
telegram: {},
|
||||
|
|
@ -71,7 +78,6 @@ describe("validateConfigObjectRawWithPlugins channel metadata", () => {
|
|||
// merge-patched value) instead of validated.config.
|
||||
setupTelegramSchemaWithDefault();
|
||||
|
||||
const { validateConfigObjectRawWithPlugins } = await import("./validation.js");
|
||||
const result = validateConfigObjectRawWithPlugins({
|
||||
channels: {
|
||||
telegram: {},
|
||||
|
|
|
|||
|
|
@ -1556,12 +1556,17 @@ describe("Cron issue regressions", () => {
|
|||
let now = dueAt;
|
||||
let activeRuns = 0;
|
||||
let peakActiveRuns = 0;
|
||||
const firstStarted = createDeferred<void>();
|
||||
const firstRun = createDeferred<{ status: "ok"; summary: string }>();
|
||||
const secondRun = createDeferred<{ status: "ok"; summary: string }>();
|
||||
const secondStarted = createDeferred<void>();
|
||||
const bothFinished = createDeferred<void>();
|
||||
const runIsolatedAgentJob = vi.fn(async (params: { job: { id: string } }) => {
|
||||
activeRuns += 1;
|
||||
peakActiveRuns = Math.max(peakActiveRuns, activeRuns);
|
||||
if (params.job.id === first.id) {
|
||||
firstStarted.resolve();
|
||||
}
|
||||
if (params.job.id === second.id) {
|
||||
secondStarted.resolve();
|
||||
}
|
||||
|
|
@ -1583,6 +1588,11 @@ describe("Cron issue regressions", () => {
|
|||
enqueueSystemEvent: vi.fn(),
|
||||
requestHeartbeatNow: vi.fn(),
|
||||
runIsolatedAgentJob,
|
||||
onEvent: (evt) => {
|
||||
if (evt.action === "finished" && evt.jobId === second.id && evt.status === "ok") {
|
||||
bothFinished.resolve();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const firstAck = await enqueueRun(state, first.id, "force");
|
||||
|
|
@ -1590,7 +1600,7 @@ describe("Cron issue regressions", () => {
|
|||
expect(firstAck).toEqual({ ok: true, enqueued: true, runId: expect.any(String) });
|
||||
expect(secondAck).toEqual({ ok: true, enqueued: true, runId: expect.any(String) });
|
||||
|
||||
await vi.waitFor(() => expect(runIsolatedAgentJob).toHaveBeenCalledTimes(1));
|
||||
await firstStarted.promise;
|
||||
expect(runIsolatedAgentJob.mock.calls[0]?.[0]).toMatchObject({ job: { id: first.id } });
|
||||
expect(peakActiveRuns).toBe(1);
|
||||
|
||||
|
|
@ -1601,11 +1611,10 @@ describe("Cron issue regressions", () => {
|
|||
expect(peakActiveRuns).toBe(1);
|
||||
|
||||
secondRun.resolve({ status: "ok", summary: "second queued run" });
|
||||
await vi.waitFor(() => {
|
||||
const jobs = state.store?.jobs ?? [];
|
||||
expect(jobs.find((job) => job.id === first.id)?.state.lastStatus).toBe("ok");
|
||||
expect(jobs.find((job) => job.id === second.id)?.state.lastStatus).toBe("ok");
|
||||
});
|
||||
await bothFinished.promise;
|
||||
const jobs = state.store?.jobs ?? [];
|
||||
expect(jobs.find((job) => job.id === first.id)?.state.lastStatus).toBe("ok");
|
||||
expect(jobs.find((job) => job.id === second.id)?.state.lastStatus).toBe("ok");
|
||||
|
||||
clearCommandLane(CommandLane.Cron);
|
||||
});
|
||||
|
|
@ -1618,6 +1627,10 @@ describe("Cron issue regressions", () => {
|
|||
const dueAt = Date.parse("2026-02-06T10:05:03.000Z");
|
||||
const job = createDueIsolatedJob({ id: "queued-failure", nowMs: dueAt, nextRunAtMs: dueAt });
|
||||
const log = createNoopLogger();
|
||||
const errorLogged = createDeferred<void>();
|
||||
log.error.mockImplementation(() => {
|
||||
errorLogged.resolve();
|
||||
});
|
||||
const badStore = `${makeStorePath().storePath}.dir`;
|
||||
await fs.mkdir(badStore, { recursive: true });
|
||||
const state = createRunningCronServiceState({
|
||||
|
|
@ -1630,7 +1643,8 @@ describe("Cron issue regressions", () => {
|
|||
const result = await enqueueRun(state, job.id, "force");
|
||||
expect(result).toEqual({ ok: true, enqueued: true, runId: expect.any(String) });
|
||||
|
||||
await vi.waitFor(() => expect(log.error).toHaveBeenCalledTimes(1));
|
||||
await errorLogged.promise;
|
||||
expect(log.error).toHaveBeenCalledTimes(1);
|
||||
expect(log.error.mock.calls[0]?.[1]).toBe(
|
||||
"cron: queued manual run background execution failed",
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,21 +1,22 @@
|
|||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, describe, expect, it, vi } from "vitest";
|
||||
|
||||
afterEach(() => {
|
||||
vi.resetModules();
|
||||
vi.doUnmock("./launchd.js");
|
||||
});
|
||||
const resolveGatewayLogPathsMock = vi.fn(() => ({
|
||||
stdoutPath: "C:\\tmp\\openclaw-state\\logs\\gateway.log",
|
||||
stderrPath: "C:\\tmp\\openclaw-state\\logs\\gateway.err.log",
|
||||
}));
|
||||
|
||||
vi.mock("./launchd.js", () => ({
|
||||
resolveGatewayLogPaths: resolveGatewayLogPathsMock,
|
||||
}));
|
||||
|
||||
let buildPlatformRuntimeLogHints: typeof import("./runtime-hints.js").buildPlatformRuntimeLogHints;
|
||||
|
||||
describe("buildPlatformRuntimeLogHints", () => {
|
||||
it("strips windows drive prefixes from darwin display paths", async () => {
|
||||
vi.doMock("./launchd.js", () => ({
|
||||
resolveGatewayLogPaths: () => ({
|
||||
stdoutPath: "C:\\tmp\\openclaw-state\\logs\\gateway.log",
|
||||
stderrPath: "C:\\tmp\\openclaw-state\\logs\\gateway.err.log",
|
||||
}),
|
||||
}));
|
||||
|
||||
const { buildPlatformRuntimeLogHints } = await import("./runtime-hints.js");
|
||||
beforeAll(async () => {
|
||||
({ buildPlatformRuntimeLogHints } = await import("./runtime-hints.js"));
|
||||
});
|
||||
|
||||
it("strips windows drive prefixes from darwin display paths", () => {
|
||||
expect(
|
||||
buildPlatformRuntimeLogHints({
|
||||
platform: "darwin",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { execSchtasks } from "./schtasks-exec.js";
|
||||
|
||||
const runCommandWithTimeout = vi.hoisted(() => vi.fn());
|
||||
|
||||
|
|
@ -6,8 +7,6 @@ vi.mock("../process/exec.js", () => ({
|
|||
runCommandWithTimeout: (...args: unknown[]) => runCommandWithTimeout(...args),
|
||||
}));
|
||||
|
||||
const { execSchtasks } = await import("./schtasks-exec.js");
|
||||
|
||||
beforeEach(() => {
|
||||
runCommandWithTimeout.mockReset();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import { splitArgsPreservingQuotes } from "./arg-split.js";
|
|||
import { parseSystemdExecStart } from "./systemd-unit.js";
|
||||
import {
|
||||
isNonFatalSystemdInstallProbeError,
|
||||
isSystemdServiceEnabled,
|
||||
isSystemdUserServiceAvailable,
|
||||
parseSystemdShow,
|
||||
readSystemdServiceExecStart,
|
||||
|
|
@ -71,7 +72,6 @@ function assertMachineUserSystemctlArgs(args: string[], user: string, ...command
|
|||
}
|
||||
|
||||
async function readManagedServiceEnabled(env: NodeJS.ProcessEnv = { HOME: TEST_MANAGED_HOME }) {
|
||||
const { isSystemdServiceEnabled } = await import("./systemd.js");
|
||||
vi.spyOn(fs, "access").mockResolvedValue(undefined);
|
||||
return isSystemdServiceEnabled({ env });
|
||||
}
|
||||
|
|
@ -180,7 +180,6 @@ describe("isSystemdServiceEnabled", () => {
|
|||
});
|
||||
|
||||
it("returns false without calling systemctl when the managed unit file is missing", async () => {
|
||||
const { isSystemdServiceEnabled } = await import("./systemd.js");
|
||||
const err = new Error("missing unit") as NodeJS.ErrnoException;
|
||||
err.code = "ENOENT";
|
||||
vi.spyOn(fs, "access").mockRejectedValueOnce(err);
|
||||
|
|
@ -286,7 +285,6 @@ describe("isSystemdServiceEnabled", () => {
|
|||
});
|
||||
|
||||
it("throws when systemctl is-enabled fails for non-state errors", async () => {
|
||||
const { isSystemdServiceEnabled } = await import("./systemd.js");
|
||||
vi.spyOn(fs, "access").mockResolvedValue(undefined);
|
||||
execFileMock
|
||||
.mockImplementationOnce((_cmd, args, _opts, cb) => {
|
||||
|
|
@ -309,7 +307,6 @@ describe("isSystemdServiceEnabled", () => {
|
|||
});
|
||||
|
||||
it("returns false when systemctl is-enabled exits with code 4 (not-found)", async () => {
|
||||
const { isSystemdServiceEnabled } = await import("./systemd.js");
|
||||
vi.spyOn(fs, "access").mockResolvedValue(undefined);
|
||||
execFileMock.mockImplementationOnce((_cmd, _args, _opts, cb) => {
|
||||
// On Ubuntu 24.04, `systemctl --user is-enabled <unit>` exits with
|
||||
|
|
|
|||
|
|
@ -98,7 +98,6 @@ describe("entry root version fast path", () => {
|
|||
const logSpy = vi.spyOn(console, "log").mockImplementation(() => {});
|
||||
|
||||
await import("./entry.js");
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(logSpy).toHaveBeenCalledWith("OpenClaw 9.9.9-test (abc1234)");
|
||||
expect(exitSpy).toHaveBeenCalledWith(0);
|
||||
|
|
@ -112,7 +111,6 @@ describe("entry root version fast path", () => {
|
|||
const logSpy = vi.spyOn(console, "log").mockImplementation(() => {});
|
||||
|
||||
await import("./entry.js");
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(logSpy).toHaveBeenCalledWith("OpenClaw 9.9.9-test");
|
||||
expect(exitSpy).toHaveBeenCalledWith(0);
|
||||
|
|
@ -126,7 +124,6 @@ describe("entry root version fast path", () => {
|
|||
const logSpy = vi.spyOn(console, "log").mockImplementation(() => {});
|
||||
|
||||
await import("./entry.js");
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(runCliMock).toHaveBeenCalledWith(["node", "openclaw", "--version"]);
|
||||
});
|
||||
|
|
@ -142,7 +139,6 @@ describe("entry root version fast path", () => {
|
|||
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
||||
|
||||
await import("./entry.js");
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(runCliMock).toHaveBeenCalledWith(["node", "openclaw", "--version"]);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
|
||||
const ensureOpenClawModelsJsonMock = vi.fn<
|
||||
|
|
@ -39,14 +39,21 @@ vi.mock("../agents/pi-embedded-runner/model.js", () => ({
|
|||
) => resolveModelMock(provider, modelId, agentDir, cfg, options),
|
||||
}));
|
||||
|
||||
let prewarmConfiguredPrimaryModel: typeof import("./server-startup.js").__testing.prewarmConfiguredPrimaryModel;
|
||||
|
||||
describe("gateway startup primary model warmup", () => {
|
||||
beforeAll(async () => {
|
||||
({
|
||||
__testing: { prewarmConfiguredPrimaryModel },
|
||||
} = await import("./server-startup.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
ensureOpenClawModelsJsonMock.mockClear();
|
||||
resolveModelMock.mockClear();
|
||||
});
|
||||
|
||||
it("prewarms an explicit configured primary model", async () => {
|
||||
const { __testing } = await import("./server-startup.js");
|
||||
const cfg = {
|
||||
agents: {
|
||||
defaults: {
|
||||
|
|
@ -57,7 +64,7 @@ describe("gateway startup primary model warmup", () => {
|
|||
},
|
||||
} as OpenClawConfig;
|
||||
|
||||
await __testing.prewarmConfiguredPrimaryModel({
|
||||
await prewarmConfiguredPrimaryModel({
|
||||
cfg,
|
||||
log: { warn: vi.fn() },
|
||||
});
|
||||
|
|
@ -69,9 +76,7 @@ describe("gateway startup primary model warmup", () => {
|
|||
});
|
||||
|
||||
it("skips warmup when no explicit primary model is configured", async () => {
|
||||
const { __testing } = await import("./server-startup.js");
|
||||
|
||||
await __testing.prewarmConfiguredPrimaryModel({
|
||||
await prewarmConfiguredPrimaryModel({
|
||||
cfg: {} as OpenClawConfig,
|
||||
log: { warn: vi.fn() },
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
|
||||
import { createEmptyPluginRegistry } from "../plugins/registry.js";
|
||||
|
||||
const { resolveRuntimePluginRegistryMock } = vi.hoisted(() => ({
|
||||
|
|
@ -15,17 +15,16 @@ let getImageGenerationProvider: typeof import("./provider-registry.js").getImage
|
|||
let listImageGenerationProviders: typeof import("./provider-registry.js").listImageGenerationProviders;
|
||||
|
||||
describe("image-generation provider registry", () => {
|
||||
beforeAll(async () => {
|
||||
({ getImageGenerationProvider, listImageGenerationProviders } =
|
||||
await import("./provider-registry.js"));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resolveRuntimePluginRegistryMock.mockReset();
|
||||
resolveRuntimePluginRegistryMock.mockReturnValue(undefined);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ getImageGenerationProvider, listImageGenerationProviders } =
|
||||
await import("./provider-registry.js"));
|
||||
});
|
||||
|
||||
it("does not load plugins when listing without config", () => {
|
||||
expect(listImageGenerationProviders()).toEqual([]);
|
||||
expect(resolveRuntimePluginRegistryMock).toHaveBeenCalledWith();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { createEmptyPluginRegistry } from "../plugins/registry.js";
|
||||
import { resetPluginRuntimeStateForTest, setActivePluginRegistry } from "../plugins/runtime.js";
|
||||
|
|
@ -24,20 +24,18 @@ function setCompatibleActiveImageGenerationRegistry(
|
|||
}
|
||||
|
||||
describe("image-generation runtime helpers", () => {
|
||||
beforeAll(async () => {
|
||||
({ generateImage, listRuntimeImageGenerationProviders } = await import("./runtime.js"));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resolveRuntimePluginRegistryMock.mockReset();
|
||||
resolveRuntimePluginRegistryMock.mockReturnValue(undefined);
|
||||
resetPluginRuntimeStateForTest();
|
||||
vi.doUnmock("../plugins/loader.js");
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
beforeEach(() => {
|
||||
resetPluginRuntimeStateForTest();
|
||||
vi.doMock("../plugins/loader.js", () => ({
|
||||
resolveRuntimePluginRegistry: resolveRuntimePluginRegistryMock,
|
||||
}));
|
||||
({ generateImage, listRuntimeImageGenerationProviders } = await import("./runtime.js"));
|
||||
});
|
||||
|
||||
it("generates images through the active image-generation registry", async () => {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ beforeEach(async () => {
|
|||
vi.resetModules();
|
||||
({ isTruthyEnvValue, logAcceptedEnvOption, normalizeEnv, normalizeZaiEnv } =
|
||||
await import("./env.js"));
|
||||
loggerMocks.info.mockClear();
|
||||
});
|
||||
|
||||
describe("normalizeZaiEnv", () => {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,11 @@ afterEach(() => {
|
|||
|
||||
const emptyRegistry = createTestRegistry([]);
|
||||
|
||||
async function flushPendingDelivery(): Promise<void> {
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
}
|
||||
|
||||
function isDiscordExecApprovalClientEnabledForTest(params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string | null;
|
||||
|
|
@ -368,9 +373,8 @@ describe("exec approval forwarder", () => {
|
|||
|
||||
const { deliver, forwarder } = createForwarder({ cfg: TARGETS_CFG });
|
||||
await expect(forwarder.handleRequested(baseRequest)).resolves.toBe(true);
|
||||
await vi.waitFor(() => {
|
||||
expect(deliver).toHaveBeenCalled();
|
||||
});
|
||||
await flushPendingDelivery();
|
||||
expect(deliver).toHaveBeenCalled();
|
||||
expect(beforeDeliverPayload).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
hint: { kind: "approval-pending", approvalKind: "exec" },
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const loadConfigMock = vi.hoisted(() => vi.fn());
|
||||
const getChannelPluginMock = vi.hoisted(() => vi.fn());
|
||||
|
|
@ -6,47 +6,48 @@ const listChannelPluginsMock = vi.hoisted(() => vi.fn());
|
|||
const isDeliverableMessageChannelMock = vi.hoisted(() => vi.fn());
|
||||
const normalizeMessageChannelMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock("../config/config.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("../config/config.js")>();
|
||||
return {
|
||||
...actual,
|
||||
loadConfig: (...args: unknown[]) => loadConfigMock(...args),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("../channels/plugins/index.js", () => ({
|
||||
getChannelPlugin: (...args: unknown[]) => getChannelPluginMock(...args),
|
||||
listChannelPlugins: (...args: unknown[]) => listChannelPluginsMock(...args),
|
||||
}));
|
||||
|
||||
vi.mock("../utils/message-channel.js", () => ({
|
||||
INTERNAL_MESSAGE_CHANNEL: "web",
|
||||
isDeliverableMessageChannel: (...args: unknown[]) => isDeliverableMessageChannelMock(...args),
|
||||
normalizeMessageChannel: (...args: unknown[]) => normalizeMessageChannelMock(...args),
|
||||
}));
|
||||
|
||||
type ExecApprovalSurfaceModule = typeof import("./exec-approval-surface.js");
|
||||
|
||||
let hasConfiguredExecApprovalDmRoute: ExecApprovalSurfaceModule["hasConfiguredExecApprovalDmRoute"];
|
||||
let resolveExecApprovalInitiatingSurfaceState: ExecApprovalSurfaceModule["resolveExecApprovalInitiatingSurfaceState"];
|
||||
|
||||
async function loadExecApprovalSurfaceModule() {
|
||||
vi.resetModules();
|
||||
loadConfigMock.mockReset();
|
||||
getChannelPluginMock.mockReset();
|
||||
listChannelPluginsMock.mockReset();
|
||||
isDeliverableMessageChannelMock.mockReset();
|
||||
normalizeMessageChannelMock.mockReset();
|
||||
normalizeMessageChannelMock.mockImplementation((value?: string | null) =>
|
||||
typeof value === "string" ? value.trim().toLowerCase() : undefined,
|
||||
);
|
||||
isDeliverableMessageChannelMock.mockImplementation(
|
||||
(value?: string) => value === "slack" || value === "discord" || value === "telegram",
|
||||
);
|
||||
vi.doMock("../config/config.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("../config/config.js")>();
|
||||
return {
|
||||
...actual,
|
||||
loadConfig: (...args: unknown[]) => loadConfigMock(...args),
|
||||
};
|
||||
});
|
||||
vi.doMock("../channels/plugins/index.js", () => ({
|
||||
getChannelPlugin: (...args: unknown[]) => getChannelPluginMock(...args),
|
||||
listChannelPlugins: (...args: unknown[]) => listChannelPluginsMock(...args),
|
||||
}));
|
||||
vi.doMock("../utils/message-channel.js", () => ({
|
||||
INTERNAL_MESSAGE_CHANNEL: "web",
|
||||
isDeliverableMessageChannel: (...args: unknown[]) => isDeliverableMessageChannelMock(...args),
|
||||
normalizeMessageChannel: (...args: unknown[]) => normalizeMessageChannelMock(...args),
|
||||
}));
|
||||
({ hasConfiguredExecApprovalDmRoute, resolveExecApprovalInitiatingSurfaceState } =
|
||||
await import("./exec-approval-surface.js"));
|
||||
}
|
||||
|
||||
describe("resolveExecApprovalInitiatingSurfaceState", () => {
|
||||
beforeEach(async () => {
|
||||
await loadExecApprovalSurfaceModule();
|
||||
beforeAll(async () => {
|
||||
({ hasConfiguredExecApprovalDmRoute, resolveExecApprovalInitiatingSurfaceState } =
|
||||
await import("./exec-approval-surface.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
loadConfigMock.mockReset();
|
||||
getChannelPluginMock.mockReset();
|
||||
listChannelPluginsMock.mockReset();
|
||||
isDeliverableMessageChannelMock.mockReset();
|
||||
normalizeMessageChannelMock.mockReset();
|
||||
normalizeMessageChannelMock.mockImplementation((value?: string | null) =>
|
||||
typeof value === "string" ? value.trim().toLowerCase() : undefined,
|
||||
);
|
||||
isDeliverableMessageChannelMock.mockImplementation(
|
||||
(value?: string) => value === "slack" || value === "discord" || value === "telegram",
|
||||
);
|
||||
});
|
||||
|
||||
it.each([
|
||||
|
|
@ -163,8 +164,18 @@ describe("resolveExecApprovalInitiatingSurfaceState", () => {
|
|||
});
|
||||
|
||||
describe("hasConfiguredExecApprovalDmRoute", () => {
|
||||
beforeEach(async () => {
|
||||
await loadExecApprovalSurfaceModule();
|
||||
beforeEach(() => {
|
||||
loadConfigMock.mockReset();
|
||||
getChannelPluginMock.mockReset();
|
||||
listChannelPluginsMock.mockReset();
|
||||
isDeliverableMessageChannelMock.mockReset();
|
||||
normalizeMessageChannelMock.mockReset();
|
||||
normalizeMessageChannelMock.mockImplementation((value?: string | null) =>
|
||||
typeof value === "string" ? value.trim().toLowerCase() : undefined,
|
||||
);
|
||||
isDeliverableMessageChannelMock.mockImplementation(
|
||||
(value?: string) => value === "slack" || value === "discord" || value === "telegram",
|
||||
);
|
||||
});
|
||||
|
||||
it.each([
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { makeTempDir } from "./exec-approvals-test-helpers.js";
|
||||
|
||||
const requestJsonlSocketMock = vi.hoisted(() => vi.fn());
|
||||
|
|
@ -26,8 +26,7 @@ let resolveExecApprovalsSocketPath: ExecApprovalsModule["resolveExecApprovalsSoc
|
|||
const tempDirs: string[] = [];
|
||||
const originalOpenClawHome = process.env.OPENCLAW_HOME;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
beforeAll(async () => {
|
||||
({
|
||||
addAllowlistEntry,
|
||||
ensureExecApprovals,
|
||||
|
|
@ -39,6 +38,9 @@ beforeEach(async () => {
|
|||
resolveExecApprovalsPath,
|
||||
resolveExecApprovalsSocketPath,
|
||||
} = await import("./exec-approvals.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
requestJsonlSocketMock.mockReset();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const PROXY_ENV_KEYS = [
|
||||
"HTTPS_PROXY",
|
||||
|
|
@ -75,13 +75,15 @@ function restoreProxyEnv(): void {
|
|||
}
|
||||
|
||||
describe("makeProxyFetch", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
vi.clearAllMocks();
|
||||
beforeAll(async () => {
|
||||
({ getProxyUrlFromFetch, makeProxyFetch, PROXY_FETCH_PROXY_URL, resolveProxyFetchFromEnv } =
|
||||
await import("./proxy-fetch.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("uses undici fetch with ProxyAgent dispatcher", async () => {
|
||||
const proxyUrl = "http://proxy.test:8080";
|
||||
undiciFetch.mockResolvedValue({ ok: true });
|
||||
|
|
@ -216,5 +218,4 @@ afterAll(() => {
|
|||
for (const id of mockedModuleIds) {
|
||||
vi.doUnmock(id);
|
||||
}
|
||||
vi.resetModules();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { TEST_UNDICI_RUNTIME_DEPS_KEY } from "./undici-runtime.js";
|
||||
|
||||
const { agentCtor, envHttpProxyAgentCtor, proxyAgentCtor } = vi.hoisted(() => ({
|
||||
|
|
@ -20,8 +20,11 @@ import type { PinnedHostname } from "./ssrf.js";
|
|||
|
||||
let createPinnedDispatcher: typeof import("./ssrf.js").createPinnedDispatcher;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
beforeAll(async () => {
|
||||
({ createPinnedDispatcher } = await import("./ssrf.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
agentCtor.mockClear();
|
||||
envHttpProxyAgentCtor.mockClear();
|
||||
proxyAgentCtor.mockClear();
|
||||
|
|
@ -30,7 +33,6 @@ beforeEach(async () => {
|
|||
EnvHttpProxyAgent: envHttpProxyAgentCtor,
|
||||
ProxyAgent: proxyAgentCtor,
|
||||
};
|
||||
({ createPinnedDispatcher } = await import("./ssrf.js"));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { afterAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const {
|
||||
Agent,
|
||||
|
|
@ -75,14 +75,16 @@ let ensureGlobalUndiciStreamTimeouts: typeof import("./undici-global-dispatcher.
|
|||
let resetGlobalUndiciStreamTimeoutsForTests: typeof import("./undici-global-dispatcher.js").resetGlobalUndiciStreamTimeoutsForTests;
|
||||
|
||||
describe("ensureGlobalUndiciStreamTimeouts", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
beforeAll(async () => {
|
||||
({
|
||||
DEFAULT_UNDICI_STREAM_TIMEOUT_MS,
|
||||
ensureGlobalUndiciEnvProxyDispatcher,
|
||||
ensureGlobalUndiciStreamTimeouts,
|
||||
resetGlobalUndiciStreamTimeoutsForTests,
|
||||
} = await import("./undici-global-dispatcher.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
resetGlobalUndiciStreamTimeoutsForTests();
|
||||
setCurrentDispatcher(new Agent());
|
||||
|
|
@ -238,5 +240,4 @@ afterAll(() => {
|
|||
for (const id of mockedModuleIds) {
|
||||
vi.doUnmock(id);
|
||||
}
|
||||
vi.resetModules();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
resolveOutboundTarget: vi.fn(() => ({ ok: true as const, to: "+1999" })),
|
||||
|
|
@ -73,9 +73,11 @@ import type { OpenClawConfig } from "../../config/config.js";
|
|||
let resolveAgentDeliveryPlan: typeof import("./agent-delivery.js").resolveAgentDeliveryPlan;
|
||||
let resolveAgentOutboundTarget: typeof import("./agent-delivery.js").resolveAgentOutboundTarget;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
beforeAll(async () => {
|
||||
({ resolveAgentDeliveryPlan, resolveAgentOutboundTarget } = await import("./agent-delivery.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
mocks.resolveOutboundTarget.mockClear();
|
||||
mocks.resolveSessionDeliveryTarget.mockClear();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
listChannelPlugins: vi.fn(),
|
||||
|
|
@ -21,8 +21,7 @@ let listConfiguredMessageChannels: ChannelSelectionModule["listConfiguredMessage
|
|||
let resolveMessageChannelSelection: ChannelSelectionModule["resolveMessageChannelSelection"];
|
||||
let runtimeModule: RuntimeModule;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
beforeAll(async () => {
|
||||
runtimeModule = await import("../../runtime.js");
|
||||
({ __testing, listConfiguredMessageChannels, resolveMessageChannelSelection } =
|
||||
await import("./channel-selection.js"));
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const resolveAgentIdentityMock = vi.hoisted(() => vi.fn());
|
||||
const resolveAgentAvatarMock = vi.hoisted(() => vi.fn());
|
||||
|
|
@ -16,11 +16,15 @@ type IdentityModule = typeof import("./identity.js");
|
|||
let normalizeOutboundIdentity: IdentityModule["normalizeOutboundIdentity"];
|
||||
let resolveAgentOutboundIdentity: IdentityModule["resolveAgentOutboundIdentity"];
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
beforeAll(async () => {
|
||||
({ normalizeOutboundIdentity, resolveAgentOutboundIdentity } = await import("./identity.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
resolveAgentIdentityMock.mockReset();
|
||||
resolveAgentAvatarMock.mockReset();
|
||||
});
|
||||
|
||||
describe("normalizeOutboundIdentity", () => {
|
||||
it.each([
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { ChannelOutboundAdapter, ChannelPlugin } from "../../channels/plugins/types.js";
|
||||
import { setActivePluginRegistry } from "../../plugins/runtime.js";
|
||||
import {
|
||||
|
|
@ -22,9 +22,11 @@ vi.mock("../../gateway/call.js", () => ({
|
|||
let sendMessage: typeof import("./message.js").sendMessage;
|
||||
let sendPoll: typeof import("./message.js").sendPoll;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
beforeAll(async () => {
|
||||
({ sendMessage, sendPoll } = await import("./message.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
callGatewayMock.mockClear();
|
||||
setRegistry(emptyRegistry);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
getChannelPlugin: vi.fn(),
|
||||
|
|
@ -7,46 +7,58 @@ const mocks = vi.hoisted(() => ({
|
|||
resolveRuntimePluginRegistry: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../../channels/plugins/index.js", () => ({
|
||||
normalizeChannelId: (channel?: string) => channel?.trim().toLowerCase() ?? undefined,
|
||||
getChannelPlugin: mocks.getChannelPlugin,
|
||||
listChannelPlugins: () => [],
|
||||
}));
|
||||
|
||||
vi.mock("../../agents/agent-scope.js", () => ({
|
||||
resolveDefaultAgentId: () => "main",
|
||||
resolveSessionAgentId: ({
|
||||
sessionKey,
|
||||
}: {
|
||||
sessionKey?: string;
|
||||
config?: unknown;
|
||||
agentId?: string;
|
||||
}) => {
|
||||
const match = sessionKey?.match(/^agent:([^:]+)/i);
|
||||
return match?.[1] ?? "main";
|
||||
},
|
||||
resolveAgentWorkspaceDir: () => "/tmp/openclaw-test-workspace",
|
||||
}));
|
||||
|
||||
vi.mock("../../config/plugin-auto-enable.js", () => ({
|
||||
applyPluginAutoEnable: ({ config }: { config: unknown }) => ({ config, changes: [] }),
|
||||
}));
|
||||
|
||||
vi.mock("../../plugins/loader.js", () => ({
|
||||
resolveRuntimePluginRegistry: mocks.resolveRuntimePluginRegistry,
|
||||
}));
|
||||
|
||||
vi.mock("./targets.js", () => ({
|
||||
resolveOutboundTarget: mocks.resolveOutboundTarget,
|
||||
}));
|
||||
|
||||
vi.mock("./deliver.js", () => ({
|
||||
deliverOutboundPayloads: mocks.deliverOutboundPayloads,
|
||||
}));
|
||||
|
||||
import { setActivePluginRegistry } from "../../plugins/runtime.js";
|
||||
import { createTestRegistry } from "../../test-utils/channel-plugins.js";
|
||||
|
||||
let sendMessage: typeof import("./message.js").sendMessage;
|
||||
let resetOutboundChannelResolutionStateForTest: typeof import("./channel-resolution.js").resetOutboundChannelResolutionStateForTest;
|
||||
|
||||
describe("sendMessage", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
vi.doMock("../../channels/plugins/index.js", () => ({
|
||||
normalizeChannelId: (channel?: string) => channel?.trim().toLowerCase() ?? undefined,
|
||||
getChannelPlugin: mocks.getChannelPlugin,
|
||||
listChannelPlugins: () => [],
|
||||
}));
|
||||
vi.doMock("../../agents/agent-scope.js", () => ({
|
||||
resolveDefaultAgentId: () => "main",
|
||||
resolveSessionAgentId: ({
|
||||
sessionKey,
|
||||
}: {
|
||||
sessionKey?: string;
|
||||
config?: unknown;
|
||||
agentId?: string;
|
||||
}) => {
|
||||
const match = sessionKey?.match(/^agent:([^:]+)/i);
|
||||
return match?.[1] ?? "main";
|
||||
},
|
||||
resolveAgentWorkspaceDir: () => "/tmp/openclaw-test-workspace",
|
||||
}));
|
||||
vi.doMock("../../config/plugin-auto-enable.js", () => ({
|
||||
applyPluginAutoEnable: ({ config }: { config: unknown }) => ({ config, changes: [] }),
|
||||
}));
|
||||
vi.doMock("../../plugins/loader.js", () => ({
|
||||
resolveRuntimePluginRegistry: mocks.resolveRuntimePluginRegistry,
|
||||
}));
|
||||
vi.doMock("./targets.js", () => ({
|
||||
resolveOutboundTarget: mocks.resolveOutboundTarget,
|
||||
}));
|
||||
vi.doMock("./deliver.js", () => ({
|
||||
deliverOutboundPayloads: mocks.deliverOutboundPayloads,
|
||||
}));
|
||||
beforeAll(async () => {
|
||||
({ sendMessage } = await import("./message.js"));
|
||||
({ resetOutboundChannelResolutionStateForTest } = await import("./channel-resolution.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
setActivePluginRegistry(createTestRegistry([]));
|
||||
resetOutboundChannelResolutionStateForTest();
|
||||
mocks.getChannelPlugin.mockClear();
|
||||
mocks.resolveOutboundTarget.mockClear();
|
||||
mocks.deliverOutboundPayloads.mockClear();
|
||||
|
|
@ -57,8 +69,6 @@ describe("sendMessage", () => {
|
|||
});
|
||||
mocks.resolveOutboundTarget.mockImplementation(({ to }: { to: string }) => ({ ok: true, to }));
|
||||
mocks.deliverOutboundPayloads.mockResolvedValue([{ channel: "mattermost", messageId: "m1" }]);
|
||||
|
||||
({ sendMessage } = await import("./message.js"));
|
||||
});
|
||||
|
||||
it("passes explicit agentId to outbound delivery for scoped media roots", async () => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Container, Separator, TextDisplay } from "@buape/carbon";
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
import { vi } from "vitest";
|
||||
import type { ChannelMessageActionName } from "../../channels/plugins/types.js";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
|
|
@ -53,6 +53,19 @@ const mocks = vi.hoisted(() => ({
|
|||
),
|
||||
}));
|
||||
|
||||
vi.mock("./channel-adapters.js", () => ({
|
||||
getChannelMessageAdapter: mocks.getChannelMessageAdapter,
|
||||
}));
|
||||
|
||||
vi.mock("./target-normalization.js", () => ({
|
||||
normalizeTargetForProvider: mocks.normalizeTargetForProvider,
|
||||
}));
|
||||
|
||||
vi.mock("./target-resolver.js", () => ({
|
||||
formatTargetDisplay: mocks.formatTargetDisplay,
|
||||
lookupDirectoryDisplay: mocks.lookupDirectoryDisplay,
|
||||
}));
|
||||
|
||||
const slackConfig = {
|
||||
channels: {
|
||||
slack: {
|
||||
|
|
@ -96,19 +109,7 @@ function expectCrossContextPolicyResult(params: {
|
|||
}
|
||||
|
||||
describe("outbound policy helpers", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
vi.clearAllMocks();
|
||||
vi.doMock("./channel-adapters.js", () => ({
|
||||
getChannelMessageAdapter: mocks.getChannelMessageAdapter,
|
||||
}));
|
||||
vi.doMock("./target-normalization.js", () => ({
|
||||
normalizeTargetForProvider: mocks.normalizeTargetForProvider,
|
||||
}));
|
||||
vi.doMock("./target-resolver.js", () => ({
|
||||
formatTargetDisplay: mocks.formatTargetDisplay,
|
||||
lookupDirectoryDisplay: mocks.lookupDirectoryDisplay,
|
||||
}));
|
||||
beforeAll(async () => {
|
||||
({
|
||||
applyCrossContextDecoration,
|
||||
buildCrossContextDecoration,
|
||||
|
|
@ -117,6 +118,10 @@ describe("outbound policy helpers", () => {
|
|||
} = await import("./outbound-policy.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
cfg: {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const resolveSessionAgentIdMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
|
|
@ -6,15 +6,18 @@ type SessionContextModule = typeof import("./session-context.js");
|
|||
|
||||
let buildOutboundSessionContext: SessionContextModule["buildOutboundSessionContext"];
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
resolveSessionAgentIdMock.mockReset();
|
||||
vi.doMock("../../agents/agent-scope.js", () => ({
|
||||
resolveSessionAgentId: (...args: unknown[]) => resolveSessionAgentIdMock(...args),
|
||||
}));
|
||||
vi.mock("../../agents/agent-scope.js", () => ({
|
||||
resolveSessionAgentId: (...args: unknown[]) => resolveSessionAgentIdMock(...args),
|
||||
}));
|
||||
|
||||
beforeAll(async () => {
|
||||
({ buildOutboundSessionContext } = await import("./session-context.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
resolveSessionAgentIdMock.mockReset();
|
||||
});
|
||||
|
||||
describe("buildOutboundSessionContext", () => {
|
||||
it("returns undefined when both session key and agent id are blank", () => {
|
||||
expect(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const normalizeChannelIdMock = vi.hoisted(() => vi.fn());
|
||||
const getChannelPluginMock = vi.hoisted(() => vi.fn());
|
||||
|
|
@ -9,26 +9,31 @@ type TargetNormalizationModule = typeof import("./target-normalization.js");
|
|||
let buildTargetResolverSignature: TargetNormalizationModule["buildTargetResolverSignature"];
|
||||
let normalizeChannelTargetInput: TargetNormalizationModule["normalizeChannelTargetInput"];
|
||||
let normalizeTargetForProvider: TargetNormalizationModule["normalizeTargetForProvider"];
|
||||
let resetTargetNormalizerCacheForTests: TargetNormalizationModule["__testing"]["resetTargetNormalizerCacheForTests"];
|
||||
|
||||
async function loadTargetNormalizationModule() {
|
||||
vi.doMock("../../channels/plugins/index.js", () => ({
|
||||
normalizeChannelId: (...args: unknown[]) => normalizeChannelIdMock(...args),
|
||||
getChannelPlugin: (...args: unknown[]) => getChannelPluginMock(...args),
|
||||
}));
|
||||
vi.doMock("../../plugins/runtime.js", () => ({
|
||||
getActivePluginChannelRegistryVersion: (...args: unknown[]) =>
|
||||
getActivePluginChannelRegistryVersionMock(...args),
|
||||
}));
|
||||
vi.mock("../../channels/plugins/index.js", () => ({
|
||||
normalizeChannelId: (...args: unknown[]) => normalizeChannelIdMock(...args),
|
||||
getChannelPlugin: (...args: unknown[]) => getChannelPluginMock(...args),
|
||||
}));
|
||||
|
||||
vi.mock("../../plugins/runtime.js", () => ({
|
||||
getActivePluginChannelRegistryVersion: (...args: unknown[]) =>
|
||||
getActivePluginChannelRegistryVersionMock(...args),
|
||||
}));
|
||||
|
||||
beforeAll(async () => {
|
||||
({ buildTargetResolverSignature, normalizeChannelTargetInput, normalizeTargetForProvider } =
|
||||
await import("./target-normalization.js"));
|
||||
}
|
||||
({
|
||||
__testing: { resetTargetNormalizerCacheForTests },
|
||||
} = await import("./target-normalization.js"));
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
beforeEach(() => {
|
||||
normalizeChannelIdMock.mockReset();
|
||||
getChannelPluginMock.mockReset();
|
||||
getActivePluginChannelRegistryVersionMock.mockReset();
|
||||
await loadTargetNormalizationModule();
|
||||
resetTargetNormalizerCacheForTests();
|
||||
});
|
||||
|
||||
describe("normalizeChannelTargetInput", () => {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,14 @@ type TargetNormalizerCacheEntry = {
|
|||
|
||||
const targetNormalizerCacheByChannelId = new Map<string, TargetNormalizerCacheEntry>();
|
||||
|
||||
function resetTargetNormalizerCacheForTests(): void {
|
||||
targetNormalizerCacheByChannelId.clear();
|
||||
}
|
||||
|
||||
export const __testing = {
|
||||
resetTargetNormalizerCacheForTests,
|
||||
} as const;
|
||||
|
||||
function resolveTargetNormalizer(channelId: ChannelId): TargetNormalizer {
|
||||
const version = getActivePluginChannelRegistryVersion();
|
||||
const cached = targetNormalizerCacheByChannelId.get(channelId);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { ChannelDirectoryEntry } from "../../channels/plugins/types.js";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
type TargetResolverModule = typeof import("./target-resolver.js");
|
||||
|
|
@ -17,8 +17,21 @@ const mocks = vi.hoisted(() => ({
|
|||
getActivePluginChannelRegistryVersion: vi.fn(() => 1),
|
||||
}));
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
vi.mock("../../channels/plugins/index.js", () => ({
|
||||
getChannelPlugin: (...args: unknown[]) => mocks.getChannelPlugin(...args),
|
||||
normalizeChannelId: (value: string) => value,
|
||||
}));
|
||||
|
||||
vi.mock("../../plugins/runtime.js", () => ({
|
||||
getActivePluginChannelRegistryVersion: () => mocks.getActivePluginChannelRegistryVersion(),
|
||||
}));
|
||||
|
||||
beforeAll(async () => {
|
||||
({ resetDirectoryCache, resolveMessagingTarget, formatTargetDisplay } =
|
||||
await import("./target-resolver.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
mocks.listPeers.mockReset();
|
||||
mocks.listPeersLive.mockReset();
|
||||
mocks.listGroups.mockReset();
|
||||
|
|
@ -27,15 +40,7 @@ beforeEach(async () => {
|
|||
mocks.getChannelPlugin.mockReset();
|
||||
mocks.getActivePluginChannelRegistryVersion.mockReset();
|
||||
mocks.getActivePluginChannelRegistryVersion.mockReturnValue(1);
|
||||
vi.doMock("../../channels/plugins/index.js", () => ({
|
||||
getChannelPlugin: (...args: unknown[]) => mocks.getChannelPlugin(...args),
|
||||
normalizeChannelId: (value: string) => value,
|
||||
}));
|
||||
vi.doMock("../../plugins/runtime.js", () => ({
|
||||
getActivePluginChannelRegistryVersion: () => mocks.getActivePluginChannelRegistryVersion(),
|
||||
}));
|
||||
({ resetDirectoryCache, resolveMessagingTarget, formatTargetDisplay } =
|
||||
await import("./target-resolver.js"));
|
||||
resetDirectoryCache();
|
||||
});
|
||||
|
||||
async function expectOkResolution(
|
||||
|
|
|
|||
|
|
@ -1,111 +0,0 @@
|
|||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
getChannelPlugin: vi.fn(),
|
||||
resolveRuntimePluginRegistry: vi.fn(),
|
||||
}));
|
||||
|
||||
const TEST_WORKSPACE_ROOT = "/tmp/openclaw-test-workspace";
|
||||
|
||||
function normalizeChannel(value?: string) {
|
||||
return value?.trim().toLowerCase() ?? undefined;
|
||||
}
|
||||
|
||||
function applyPluginAutoEnableForTests(config: unknown) {
|
||||
return { config, changes: [] as unknown[] };
|
||||
}
|
||||
|
||||
function createTelegramPlugin() {
|
||||
return {
|
||||
id: "telegram",
|
||||
meta: { label: "Telegram" },
|
||||
config: {
|
||||
listAccountIds: () => [],
|
||||
resolveAccount: () => ({}),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
vi.mock("../../channels/plugins/index.js", () => ({
|
||||
getChannelPlugin: mocks.getChannelPlugin,
|
||||
normalizeChannelId: normalizeChannel,
|
||||
}));
|
||||
|
||||
vi.mock("../../agents/agent-scope.js", () => ({
|
||||
resolveDefaultAgentId: () => "main",
|
||||
resolveAgentWorkspaceDir: () => TEST_WORKSPACE_ROOT,
|
||||
}));
|
||||
|
||||
vi.mock("../../plugins/loader.js", () => ({
|
||||
resolveRuntimePluginRegistry: mocks.resolveRuntimePluginRegistry,
|
||||
}));
|
||||
|
||||
vi.mock("../../config/plugin-auto-enable.js", () => ({
|
||||
applyPluginAutoEnable(args: { config: unknown }) {
|
||||
return applyPluginAutoEnableForTests(args.config);
|
||||
},
|
||||
}));
|
||||
|
||||
let setActivePluginRegistry: typeof import("../../plugins/runtime.js").setActivePluginRegistry;
|
||||
let createTestRegistry: typeof import("../../test-utils/channel-plugins.js").createTestRegistry;
|
||||
let resetOutboundChannelResolutionStateForTest: typeof import("./channel-resolution.js").resetOutboundChannelResolutionStateForTest;
|
||||
let resolveOutboundTarget: typeof import("./targets.js").resolveOutboundTarget;
|
||||
|
||||
describe("resolveOutboundTarget channel resolution", () => {
|
||||
let registrySeq = 0;
|
||||
const resolveTelegramTarget = () =>
|
||||
resolveOutboundTarget({
|
||||
channel: "telegram",
|
||||
to: "123456",
|
||||
cfg: { channels: { telegram: { botToken: "test-token" } } },
|
||||
mode: "explicit",
|
||||
});
|
||||
|
||||
beforeAll(async () => {
|
||||
vi.resetModules();
|
||||
({ setActivePluginRegistry } = await import("../../plugins/runtime.js"));
|
||||
({ createTestRegistry } = await import("../../test-utils/channel-plugins.js"));
|
||||
({ resetOutboundChannelResolutionStateForTest } = await import("./channel-resolution.js"));
|
||||
({ resolveOutboundTarget } = await import("./targets.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
registrySeq += 1;
|
||||
resetOutboundChannelResolutionStateForTest();
|
||||
setActivePluginRegistry(createTestRegistry([]), `targets-test-${registrySeq}`);
|
||||
mocks.getChannelPlugin.mockReset();
|
||||
mocks.resolveRuntimePluginRegistry.mockReset();
|
||||
});
|
||||
|
||||
it("recovers telegram plugin resolution so announce delivery does not fail with Unsupported channel: telegram", () => {
|
||||
const telegramPlugin = createTelegramPlugin();
|
||||
mocks.getChannelPlugin.mockReturnValueOnce(undefined).mockReturnValueOnce(telegramPlugin);
|
||||
|
||||
const result = resolveTelegramTarget();
|
||||
|
||||
expect(result).toEqual({ ok: true, to: "123456" });
|
||||
expect(mocks.resolveRuntimePluginRegistry).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("retries bootstrap on subsequent resolve when the first bootstrap attempt fails", () => {
|
||||
const telegramPlugin = createTelegramPlugin();
|
||||
mocks.getChannelPlugin
|
||||
.mockReturnValueOnce(undefined)
|
||||
.mockReturnValueOnce(undefined)
|
||||
.mockReturnValueOnce(undefined)
|
||||
.mockReturnValueOnce(telegramPlugin)
|
||||
.mockReturnValue(telegramPlugin);
|
||||
mocks.resolveRuntimePluginRegistry
|
||||
.mockImplementationOnce(() => {
|
||||
throw new Error("bootstrap failed");
|
||||
})
|
||||
.mockImplementation(() => undefined);
|
||||
|
||||
const first = resolveTelegramTarget();
|
||||
const second = resolveTelegramTarget();
|
||||
|
||||
expect(first.ok).toBe(false);
|
||||
expect(second).toEqual({ ok: true, to: "123456" });
|
||||
expect(mocks.resolveRuntimePluginRegistry).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { Buffer } from "node:buffer";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const randomBytesMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
|
|
@ -17,12 +17,15 @@ let generatePairingToken: PairingTokenModule["generatePairingToken"];
|
|||
let PAIRING_TOKEN_BYTES: PairingTokenModule["PAIRING_TOKEN_BYTES"];
|
||||
let verifyPairingToken: PairingTokenModule["verifyPairingToken"];
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
beforeAll(async () => {
|
||||
({ generatePairingToken, PAIRING_TOKEN_BYTES, verifyPairingToken } =
|
||||
await import("./pairing-token.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
randomBytesMock.mockReset();
|
||||
});
|
||||
|
||||
describe("generatePairingToken", () => {
|
||||
it("uses the configured byte count and returns a base64url token", () => {
|
||||
randomBytesMock.mockReturnValueOnce(Buffer.from([0xfb, 0xff, 0x00]));
|
||||
|
|
|
|||
|
|
@ -61,6 +61,11 @@ function makePluginRequest(overrides?: Partial<PluginApprovalRequest>): PluginAp
|
|||
};
|
||||
}
|
||||
|
||||
async function flushPendingDelivery(): Promise<void> {
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
}
|
||||
|
||||
describe("plugin approval forwarding", () => {
|
||||
beforeEach(() => {
|
||||
setActivePluginRegistry(emptyRegistry);
|
||||
|
|
@ -78,10 +83,8 @@ describe("plugin approval forwarding", () => {
|
|||
const { forwarder } = createForwarder({ cfg: PLUGIN_TARGETS_CFG, deliver });
|
||||
const result = await forwarder.handlePluginApprovalRequested!(makePluginRequest());
|
||||
expect(result).toBe(true);
|
||||
// Allow delivery to be async
|
||||
await vi.waitFor(() => {
|
||||
expect(deliver).toHaveBeenCalled();
|
||||
});
|
||||
await flushPendingDelivery();
|
||||
expect(deliver).toHaveBeenCalled();
|
||||
const deliveryArgs = deliver.mock.calls[0]?.[0] as
|
||||
| { payloads?: Array<{ text?: string; interactive?: unknown }> }
|
||||
| undefined;
|
||||
|
|
@ -123,9 +126,8 @@ describe("plugin approval forwarding", () => {
|
|||
const request = makePluginRequest();
|
||||
request.request.severity = "critical";
|
||||
await forwarder.handlePluginApprovalRequested!(request);
|
||||
await vi.waitFor(() => {
|
||||
expect(deliver).toHaveBeenCalled();
|
||||
});
|
||||
await flushPendingDelivery();
|
||||
expect(deliver).toHaveBeenCalled();
|
||||
const text =
|
||||
(deliver.mock.calls[0]?.[0] as { payloads?: Array<{ text?: string }> })?.payloads?.[0]
|
||||
?.text ?? "";
|
||||
|
|
@ -159,9 +161,8 @@ describe("plugin approval forwarding", () => {
|
|||
const { forwarder } = createForwarder({ cfg, deliver });
|
||||
const result = await forwarder.handlePluginApprovalRequested!(makePluginRequest());
|
||||
expect(result).toBe(true);
|
||||
await vi.waitFor(() => {
|
||||
expect(deliver).toHaveBeenCalled();
|
||||
});
|
||||
await flushPendingDelivery();
|
||||
expect(deliver).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("returns false when no approvals config at all", async () => {
|
||||
|
|
@ -196,9 +197,8 @@ describe("plugin approval forwarding", () => {
|
|||
const deliver = vi.fn().mockResolvedValue([]);
|
||||
const { forwarder } = createForwarder({ cfg: PLUGIN_TARGETS_CFG, deliver });
|
||||
await forwarder.handlePluginApprovalRequested!(makePluginRequest());
|
||||
await vi.waitFor(() => {
|
||||
expect(deliver).toHaveBeenCalled();
|
||||
});
|
||||
await flushPendingDelivery();
|
||||
expect(deliver).toHaveBeenCalled();
|
||||
const deliveryArgs = deliver.mock.calls[0]?.[0] as
|
||||
| { payloads?: Array<{ text?: string }> }
|
||||
| undefined;
|
||||
|
|
@ -225,9 +225,8 @@ describe("plugin approval forwarding", () => {
|
|||
const deliver = vi.fn().mockResolvedValue([]);
|
||||
const { forwarder } = createForwarder({ cfg: PLUGIN_TARGETS_CFG, deliver });
|
||||
await forwarder.handlePluginApprovalRequested!(makePluginRequest());
|
||||
await vi.waitFor(() => {
|
||||
expect(deliver).toHaveBeenCalled();
|
||||
});
|
||||
await flushPendingDelivery();
|
||||
expect(deliver).toHaveBeenCalled();
|
||||
expect(beforeDeliverPayload).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
|
@ -256,9 +255,8 @@ describe("plugin approval forwarding", () => {
|
|||
|
||||
// First register request so targets are tracked
|
||||
await forwarder.handlePluginApprovalRequested!(makePluginRequest());
|
||||
await vi.waitFor(() => {
|
||||
expect(deliver).toHaveBeenCalled();
|
||||
});
|
||||
await flushPendingDelivery();
|
||||
expect(deliver).toHaveBeenCalled();
|
||||
deliver.mockClear();
|
||||
|
||||
const resolved: PluginApprovalResolved = {
|
||||
|
|
@ -268,6 +266,7 @@ describe("plugin approval forwarding", () => {
|
|||
ts: 2000,
|
||||
};
|
||||
await forwarder.handlePluginApprovalResolved!(resolved);
|
||||
await flushPendingDelivery();
|
||||
expect(deliver).toHaveBeenCalled();
|
||||
const deliveryArgs = deliver.mock.calls[0]?.[0] as
|
||||
| { payloads?: Array<{ text?: string }> }
|
||||
|
|
@ -283,9 +282,8 @@ describe("plugin approval forwarding", () => {
|
|||
|
||||
// First register request so targets are tracked
|
||||
await forwarder.handlePluginApprovalRequested!(makePluginRequest());
|
||||
await vi.waitFor(() => {
|
||||
expect(deliver).toHaveBeenCalled();
|
||||
});
|
||||
await flushPendingDelivery();
|
||||
expect(deliver).toHaveBeenCalled();
|
||||
deliver.mockClear();
|
||||
|
||||
const resolved: PluginApprovalResolved = {
|
||||
|
|
@ -337,10 +335,8 @@ describe("plugin approval forwarding", () => {
|
|||
const deliver = vi.fn().mockResolvedValue([]);
|
||||
const { forwarder } = createForwarder({ cfg: PLUGIN_TARGETS_CFG, deliver });
|
||||
await forwarder.handlePluginApprovalRequested!(makePluginRequest());
|
||||
// Wait for the async delivery to flush before stopping
|
||||
await vi.waitFor(() => {
|
||||
expect(deliver).toHaveBeenCalled();
|
||||
});
|
||||
await flushPendingDelivery();
|
||||
expect(deliver).toHaveBeenCalled();
|
||||
forwarder.stop();
|
||||
deliver.mockClear();
|
||||
// After stop, resolved should not deliver
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import net from "node:net";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { stripAnsi } from "../terminal/ansi.js";
|
||||
|
||||
const runCommandWithTimeoutMock = vi.hoisted(() => vi.fn());
|
||||
|
|
@ -15,12 +15,15 @@ let PortInUseError: typeof import("./ports.js").PortInUseError;
|
|||
|
||||
const describeUnix = process.platform === "win32" ? describe.skip : describe;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
beforeAll(async () => {
|
||||
({ inspectPortUsage } = await import("./ports-inspect.js"));
|
||||
({ ensurePortAvailable, handlePortError, PortInUseError } = await import("./ports.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
runCommandWithTimeoutMock.mockReset();
|
||||
});
|
||||
|
||||
describe("ports helpers", () => {
|
||||
it("ensurePortAvailable rejects when port busy", async () => {
|
||||
const server = net.createServer();
|
||||
|
|
@ -66,10 +69,6 @@ describe("ports helpers", () => {
|
|||
});
|
||||
|
||||
describeUnix("inspectPortUsage", () => {
|
||||
beforeEach(() => {
|
||||
runCommandWithTimeoutMock.mockClear();
|
||||
});
|
||||
|
||||
it("reports busy when lsof is missing but loopback listener exists", async () => {
|
||||
const server = net.createServer();
|
||||
await new Promise<void>((resolve) => server.listen(0, "127.0.0.1", resolve));
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const resolveProviderUsageAuthWithPluginMock = vi.fn(
|
||||
async (..._args: unknown[]): Promise<unknown> => null,
|
||||
|
|
@ -15,11 +15,13 @@ vi.mock("../plugins/provider-runtime.js", async (importOriginal) => {
|
|||
let resolveProviderAuths: typeof import("./provider-usage.auth.js").resolveProviderAuths;
|
||||
|
||||
describe("resolveProviderAuths plugin boundary", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
beforeAll(async () => {
|
||||
({ resolveProviderAuths } = await import("./provider-usage.auth.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
resolveProviderUsageAuthWithPluginMock.mockReset();
|
||||
resolveProviderUsageAuthWithPluginMock.mockResolvedValue(null);
|
||||
({ resolveProviderAuths } = await import("./provider-usage.auth.js"));
|
||||
});
|
||||
|
||||
it("prefers plugin-owned usage auth when available", async () => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { createProviderUsageFetch } from "../test-utils/provider-usage-fetch.js";
|
||||
|
||||
const resolveProviderUsageSnapshotWithPluginMock = vi.fn();
|
||||
|
|
@ -17,11 +17,13 @@ let loadProviderUsageSummary: typeof import("./provider-usage.load.js").loadProv
|
|||
const usageNow = Date.UTC(2026, 0, 7, 0, 0, 0);
|
||||
|
||||
describe("provider-usage.load plugin boundary", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
beforeAll(async () => {
|
||||
({ loadProviderUsageSummary } = await import("./provider-usage.load.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
resolveProviderUsageSnapshotWithPluginMock.mockReset();
|
||||
resolveProviderUsageSnapshotWithPluginMock.mockResolvedValue(null);
|
||||
({ loadProviderUsageSummary } = await import("./provider-usage.load.js"));
|
||||
});
|
||||
|
||||
it("prefers plugin-owned usage snapshots", async () => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
// This entire file tests lsof-based Unix port polling. The feature is a deliberate
|
||||
// no-op on Windows (findGatewayPidsOnPortSync returns [] immediately). Running these
|
||||
|
|
@ -86,13 +86,12 @@ function installInitialBusyPoll(
|
|||
}
|
||||
|
||||
describe.skipIf(isWindows)("restart-stale-pids", () => {
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeAll(async () => {
|
||||
({ __testing, cleanStaleGatewayProcessesSync, findGatewayPidsOnPortSync } =
|
||||
await import("./restart-stale-pids.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
mockSpawnSync.mockReset();
|
||||
mockResolveGatewayPort.mockReset();
|
||||
mockRestartWarn.mockReset();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const spawnSyncMock = vi.hoisted(() => vi.fn());
|
||||
const resolveLsofCommandSyncMock = vi.hoisted(() => vi.fn());
|
||||
|
|
@ -30,27 +30,12 @@ let findGatewayPidsOnPortSync: typeof import("./restart-stale-pids.js").findGate
|
|||
|
||||
let currentTimeMs = 0;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
vi.doMock("node:child_process", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("node:child_process")>();
|
||||
return {
|
||||
...actual,
|
||||
spawnSync: (...args: Parameters<typeof actual.spawnSync>) => spawnSyncMock(...args),
|
||||
};
|
||||
});
|
||||
vi.doMock("./ports-lsof.js", () => ({
|
||||
resolveLsofCommandSync: (...args: unknown[]) => resolveLsofCommandSyncMock(...args),
|
||||
}));
|
||||
vi.doMock("../config/paths.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("../config/paths.js")>();
|
||||
return {
|
||||
...actual,
|
||||
resolveGatewayPort: (...args: unknown[]) => resolveGatewayPortMock(...args),
|
||||
};
|
||||
});
|
||||
beforeAll(async () => {
|
||||
({ __testing, cleanStaleGatewayProcessesSync, findGatewayPidsOnPortSync } =
|
||||
await import("./restart-stale-pids.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
spawnSyncMock.mockReset();
|
||||
resolveLsofCommandSyncMock.mockReset();
|
||||
resolveGatewayPortMock.mockReset();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Buffer } from "node:buffer";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const cryptoMocks = vi.hoisted(() => ({
|
||||
randomBytes: vi.fn((bytes: number) => Buffer.alloc(bytes, 0xab)),
|
||||
|
|
@ -19,8 +19,7 @@ let generateSecureInt: typeof import("./secure-random.js").generateSecureInt;
|
|||
let generateSecureToken: typeof import("./secure-random.js").generateSecureToken;
|
||||
let generateSecureUuid: typeof import("./secure-random.js").generateSecureUuid;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
beforeAll(async () => {
|
||||
({
|
||||
generateSecureFraction,
|
||||
generateSecureHex,
|
||||
|
|
@ -30,6 +29,11 @@ beforeEach(async () => {
|
|||
} = await import("./secure-random.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cryptoMocks.randomBytes.mockClear();
|
||||
cryptoMocks.randomUUID.mockReset();
|
||||
});
|
||||
|
||||
describe("secure-random", () => {
|
||||
it("delegates UUID generation to crypto.randomUUID", () => {
|
||||
cryptoMocks.randomUUID.mockReturnValueOnce("uuid-1").mockReturnValueOnce("uuid-2");
|
||||
|
|
|
|||
|
|
@ -1,50 +1,54 @@
|
|||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const transportReadyMocks = vi.hoisted(() => ({
|
||||
injectedSleepError: null as Error | null,
|
||||
}));
|
||||
|
||||
let injectedSleepError: Error | null = null;
|
||||
type TransportReadyModule = typeof import("./transport-ready.js");
|
||||
let waitForTransportReady: TransportReadyModule["waitForTransportReady"];
|
||||
|
||||
vi.mock("./backoff.js", () => ({
|
||||
sleepWithAbort: async (ms: number, signal?: AbortSignal) => {
|
||||
if (transportReadyMocks.injectedSleepError) {
|
||||
throw transportReadyMocks.injectedSleepError;
|
||||
}
|
||||
if (signal?.aborted) {
|
||||
throw new Error("aborted");
|
||||
}
|
||||
if (ms <= 0) {
|
||||
return;
|
||||
}
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const timer = setTimeout(() => {
|
||||
signal?.removeEventListener("abort", onAbort);
|
||||
resolve();
|
||||
}, ms);
|
||||
const onAbort = () => {
|
||||
clearTimeout(timer);
|
||||
signal?.removeEventListener("abort", onAbort);
|
||||
reject(new Error("aborted"));
|
||||
};
|
||||
signal?.addEventListener("abort", onAbort, { once: true });
|
||||
});
|
||||
},
|
||||
}));
|
||||
|
||||
function createRuntime() {
|
||||
return { log: vi.fn(), error: vi.fn(), exit: vi.fn() };
|
||||
}
|
||||
|
||||
describe("waitForTransportReady", () => {
|
||||
beforeEach(async () => {
|
||||
vi.useFakeTimers();
|
||||
vi.resetModules();
|
||||
// Perf: `sleepWithAbort` uses `node:timers/promises` which isn't controlled by fake timers.
|
||||
// Route sleeps through global `setTimeout` so tests can advance time deterministically.
|
||||
vi.doMock("./backoff.js", () => ({
|
||||
sleepWithAbort: async (ms: number, signal?: AbortSignal) => {
|
||||
if (injectedSleepError) {
|
||||
throw injectedSleepError;
|
||||
}
|
||||
if (signal?.aborted) {
|
||||
throw new Error("aborted");
|
||||
}
|
||||
if (ms <= 0) {
|
||||
return;
|
||||
}
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const timer = setTimeout(() => {
|
||||
signal?.removeEventListener("abort", onAbort);
|
||||
resolve();
|
||||
}, ms);
|
||||
const onAbort = () => {
|
||||
clearTimeout(timer);
|
||||
signal?.removeEventListener("abort", onAbort);
|
||||
reject(new Error("aborted"));
|
||||
};
|
||||
signal?.addEventListener("abort", onAbort, { once: true });
|
||||
});
|
||||
},
|
||||
}));
|
||||
beforeAll(async () => {
|
||||
({ waitForTransportReady } = await import("./transport-ready.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
injectedSleepError = null;
|
||||
transportReadyMocks.injectedSleepError = null;
|
||||
});
|
||||
|
||||
it("returns when the check succeeds and logs after the delay", async () => {
|
||||
|
|
@ -154,7 +158,7 @@ describe("waitForTransportReady", () => {
|
|||
|
||||
it("rethrows non-abort sleep failures", async () => {
|
||||
const runtime = createRuntime();
|
||||
injectedSleepError = new Error("sleep exploded");
|
||||
transportReadyMocks.injectedSleepError = new Error("sleep exploded");
|
||||
|
||||
await expect(
|
||||
waitForTransportReady({
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { captureFullEnv } from "../test-utils/env.js";
|
||||
|
||||
const spawnMock = vi.hoisted(() => vi.fn());
|
||||
|
|
@ -53,11 +53,16 @@ afterEach(() => {
|
|||
});
|
||||
|
||||
describe("relaunchGatewayScheduledTask", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
beforeAll(async () => {
|
||||
({ relaunchGatewayScheduledTask } = await import("./windows-task-restart.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
spawnMock.mockReset();
|
||||
resolvePreferredOpenClawTmpDirMock.mockReset();
|
||||
resolvePreferredOpenClawTmpDirMock.mockReturnValue(os.tmpdir());
|
||||
});
|
||||
|
||||
it("writes a detached schtasks relaunch helper", () => {
|
||||
const unref = vi.fn();
|
||||
let seenCommandArg = "";
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { captureEnv } from "../test-utils/env.js";
|
||||
|
||||
const readFileSyncMock = vi.hoisted(() => vi.fn());
|
||||
|
|
@ -32,16 +32,15 @@ function setPlatform(platform: NodeJS.Platform): void {
|
|||
describe("wsl detection", () => {
|
||||
let envSnapshot: ReturnType<typeof captureEnv>;
|
||||
|
||||
beforeAll(async () => {
|
||||
({ isWSLEnv, isWSLSync, isWSL2Sync, isWSL, resetWSLStateForTests } = await import("./wsl.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
envSnapshot = captureEnv(["WSL_INTEROP", "WSL_DISTRO_NAME", "WSLENV"]);
|
||||
readFileSyncMock.mockReset();
|
||||
readFileMock.mockReset();
|
||||
setPlatform("linux");
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
({ isWSLEnv, isWSLSync, isWSL2Sync, isWSL, resetWSLStateForTests } = await import("./wsl.js"));
|
||||
resetWSLStateForTests();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { readLoggingConfig } from "./config.js";
|
||||
|
||||
const loadConfigMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
|
|
@ -24,8 +25,6 @@ describe("readLoggingConfig", () => {
|
|||
throw new Error("loadConfig should not be called");
|
||||
});
|
||||
|
||||
const { readLoggingConfig } = await import("./config.js");
|
||||
|
||||
expect(readLoggingConfig()).toBeUndefined();
|
||||
expect(loadConfigMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
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 { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import {
|
||||
withBundledPluginAllowlistCompat,
|
||||
|
|
@ -13,11 +13,22 @@ import { loadPluginManifestRegistry } from "../plugins/manifest-registry.js";
|
|||
import { createEmptyPluginRegistry } from "../plugins/registry.js";
|
||||
import { setActivePluginRegistry } from "../plugins/runtime.js";
|
||||
|
||||
const { resolveRuntimePluginRegistryMock } = vi.hoisted(() => ({
|
||||
resolveRuntimePluginRegistryMock: vi.fn<
|
||||
(params?: unknown) => ReturnType<typeof createEmptyPluginRegistry> | undefined
|
||||
>(() => undefined),
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/loader.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("../plugins/loader.js")>();
|
||||
return {
|
||||
...actual,
|
||||
resolveRuntimePluginRegistry: resolveRuntimePluginRegistryMock,
|
||||
};
|
||||
});
|
||||
|
||||
let describeImageFile: typeof import("./runtime.js").describeImageFile;
|
||||
let runMediaUnderstandingFile: typeof import("./runtime.js").runMediaUnderstandingFile;
|
||||
let resolveRuntimePluginRegistryMock: ReturnType<
|
||||
typeof vi.fn<(params?: unknown) => ReturnType<typeof createEmptyPluginRegistry> | undefined>
|
||||
>;
|
||||
|
||||
function setCompatibleActiveMediaUnderstandingRegistry(
|
||||
pluginRegistry: ReturnType<typeof createEmptyPluginRegistry>,
|
||||
|
|
@ -53,21 +64,13 @@ function setCompatibleActiveMediaUnderstandingRegistry(
|
|||
}
|
||||
|
||||
describe("media-understanding runtime helpers", () => {
|
||||
beforeAll(async () => {
|
||||
({ describeImageFile, runMediaUnderstandingFile } = await import("./runtime.js"));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resolveRuntimePluginRegistryMock.mockReset();
|
||||
resolveRuntimePluginRegistryMock.mockReturnValue(undefined);
|
||||
vi.doUnmock("../plugins/loader.js");
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
resolveRuntimePluginRegistryMock = vi.fn<
|
||||
(params?: unknown) => ReturnType<typeof createEmptyPluginRegistry> | undefined
|
||||
>(() => undefined);
|
||||
vi.doMock("../plugins/loader.js", () => ({
|
||||
resolveRuntimePluginRegistry: resolveRuntimePluginRegistryMock,
|
||||
}));
|
||||
({ describeImageFile, runMediaUnderstandingFile } = await import("./runtime.js"));
|
||||
});
|
||||
|
||||
it("describes images through the active media-understanding registry", async () => {
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@ describe("media server outside-workspace mapping", () => {
|
|||
beforeAll(async () => {
|
||||
vi.useRealTimers();
|
||||
vi.doUnmock("undici");
|
||||
vi.resetModules();
|
||||
const require = createRequire(import.meta.url);
|
||||
({ SafeOpenError } = await import("../infra/fs-safe.js"));
|
||||
({ startMediaServer } = await import("./server.js"));
|
||||
|
|
|
|||
|
|
@ -105,7 +105,6 @@ describe("media server", () => {
|
|||
beforeAll(async () => {
|
||||
vi.useRealTimers();
|
||||
vi.doUnmock("undici");
|
||||
vi.resetModules();
|
||||
const require = createRequire(import.meta.url);
|
||||
({ startMediaServer } = await import("./server.js"));
|
||||
({ MEDIA_MAX_BYTES } = await import("./store.js"));
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
||||
import { createTempHomeEnv, type TempHomeEnv } from "../test-utils/temp-home.js";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
|
|
@ -32,13 +32,9 @@ describe("media store outside-workspace mapping", () => {
|
|||
let tempHome: TempHomeEnv;
|
||||
let home = "";
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
beforeAll(async () => {
|
||||
({ saveMediaSource } = await import("./store.js"));
|
||||
({ SafeOpenError } = await import("../infra/fs-safe.js"));
|
||||
});
|
||||
|
||||
beforeAll(async () => {
|
||||
tempHome = await createTempHomeEnv("openclaw-media-store-test-home-");
|
||||
home = tempHome.home;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { SecretInput } from "../config/types.secrets.js";
|
||||
|
||||
vi.mock("../infra/device-bootstrap.js", () => ({
|
||||
|
|
@ -158,16 +158,18 @@ describe("pairing setup code", () => {
|
|||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
vi.stubEnv("OPENCLAW_GATEWAY_TOKEN", "");
|
||||
vi.stubEnv("OPENCLAW_GATEWAY_PASSWORD", "");
|
||||
vi.stubEnv("OPENCLAW_GATEWAY_PORT", "");
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeAll(async () => {
|
||||
({ encodePairingSetupCode, resolvePairingSetupFromConfig } = await import("./setup-code.js"));
|
||||
({ issueDeviceBootstrapToken: issueDeviceBootstrapTokenMock } =
|
||||
await import("../infra/device-bootstrap.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
vi.mocked(issueDeviceBootstrapTokenMock).mockClear();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -43,10 +43,10 @@ describe("enqueueKeyedTask", () => {
|
|||
},
|
||||
});
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(order).toContain("a1:start");
|
||||
expect(order).toContain("b1:start");
|
||||
});
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
expect(order).toContain("a1:start");
|
||||
expect(order).toContain("b1:start");
|
||||
expect(order).not.toContain("a2:start");
|
||||
|
||||
gate.resolve();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const loadWebMediaMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
|
|
@ -11,9 +11,11 @@ type OutboundMediaModule = typeof import("./outbound-media.js");
|
|||
let loadOutboundMediaFromUrl: OutboundMediaModule["loadOutboundMediaFromUrl"];
|
||||
|
||||
describe("loadOutboundMediaFromUrl", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
beforeAll(async () => {
|
||||
({ loadOutboundMediaFromUrl } = await import("./outbound-media.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
loadWebMediaMock.mockReset();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, describe, expect, it } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { BUNDLED_WEB_SEARCH_PLUGIN_IDS } from "./bundled-web-search-ids.js";
|
||||
import { loadPluginManifestRegistry } from "./manifest-registry.js";
|
||||
|
||||
let hasBundledWebSearchCredential: typeof import("./bundled-web-search-registry.js").hasBundledWebSearchCredential;
|
||||
let listBundledWebSearchProviders: typeof import("./bundled-web-search.js").listBundledWebSearchProviders;
|
||||
let resolveBundledWebSearchPluginIds: typeof import("./bundled-web-search.js").resolveBundledWebSearchPluginIds;
|
||||
|
||||
function resolveManifestBundledWebSearchPluginIds() {
|
||||
return loadPluginManifestRegistry({})
|
||||
.plugins.filter(
|
||||
|
|
@ -14,13 +18,18 @@ function resolveManifestBundledWebSearchPluginIds() {
|
|||
}
|
||||
|
||||
async function resolveRegistryBundledWebSearchPluginIds() {
|
||||
const { listBundledWebSearchProviders } = await import("./bundled-web-search.js");
|
||||
return listBundledWebSearchProviders()
|
||||
.map(({ pluginId }) => pluginId)
|
||||
.filter((value, index, values) => values.indexOf(value) === index)
|
||||
.toSorted((left, right) => left.localeCompare(right));
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
({ listBundledWebSearchProviders, resolveBundledWebSearchPluginIds } =
|
||||
await import("./bundled-web-search.js"));
|
||||
({ hasBundledWebSearchCredential } = await import("./bundled-web-search-registry.js"));
|
||||
});
|
||||
|
||||
function expectBundledWebSearchIds(actual: readonly string[], expected: readonly string[]) {
|
||||
expect(actual).toEqual(expected);
|
||||
}
|
||||
|
|
@ -33,12 +42,7 @@ function expectBundledWebSearchAlignment(params: {
|
|||
}
|
||||
|
||||
describe("bundled web search metadata", () => {
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
});
|
||||
|
||||
it("keeps bundled web search compat ids aligned with bundled manifests", async () => {
|
||||
const { resolveBundledWebSearchPluginIds } = await import("./bundled-web-search.js");
|
||||
expectBundledWebSearchAlignment({
|
||||
actual: resolveBundledWebSearchPluginIds({}),
|
||||
expected: resolveManifestBundledWebSearchPluginIds(),
|
||||
|
|
@ -54,10 +58,6 @@ describe("bundled web search metadata", () => {
|
|||
});
|
||||
|
||||
describe("hasBundledWebSearchCredential", () => {
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
});
|
||||
|
||||
const baseCfg = {
|
||||
agents: { defaults: { model: { primary: "ollama/mistral-8b" } } },
|
||||
browser: { enabled: false },
|
||||
|
|
@ -103,7 +103,6 @@ describe("hasBundledWebSearchCredential", () => {
|
|||
env: { OPENROUTER_API_KEY: "sk-or-v1-test" },
|
||||
},
|
||||
] as const)("$name", async ({ config, env }) => {
|
||||
const { hasBundledWebSearchCredential } = await import("./bundled-web-search-registry.js");
|
||||
expect(hasBundledWebSearchCredential({ config, env })).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { createEmptyPluginRegistry } from "./registry.js";
|
||||
|
||||
|
|
@ -132,8 +132,11 @@ function expectCompatChainApplied(params: {
|
|||
}
|
||||
|
||||
describe("resolvePluginCapabilityProviders", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
beforeAll(async () => {
|
||||
({ resolvePluginCapabilityProviders } = await import("./capability-provider-runtime.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
mocks.resolveRuntimePluginRegistry.mockReset();
|
||||
mocks.resolveRuntimePluginRegistry.mockReturnValue(undefined);
|
||||
mocks.loadPluginManifestRegistry.mockReset();
|
||||
|
|
@ -144,7 +147,6 @@ describe("resolvePluginCapabilityProviders", () => {
|
|||
mocks.withBundledPluginEnablementCompat.mockImplementation(({ config }) => config);
|
||||
mocks.withBundledPluginVitestCompat.mockReset();
|
||||
mocks.withBundledPluginVitestCompat.mockImplementation(({ config }) => config);
|
||||
({ resolvePluginCapabilityProviders } = await import("./capability-provider-runtime.js"));
|
||||
});
|
||||
|
||||
it("uses the active registry when capability providers are already loaded", () => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
|
||||
import { withEnvAsync } from "../test-utils/env.js";
|
||||
|
||||
const installPluginFromPathMock = vi.fn();
|
||||
|
|
@ -20,6 +20,9 @@ const fetchWithSsrFGuardMock = vi.hoisted(() =>
|
|||
}),
|
||||
);
|
||||
const runCommandWithTimeoutMock = vi.hoisted(() => vi.fn());
|
||||
let installPluginFromMarketplace: typeof import("./marketplace.js").installPluginFromMarketplace;
|
||||
let listMarketplacePlugins: typeof import("./marketplace.js").listMarketplacePlugins;
|
||||
let resolveMarketplaceInstallShortcut: typeof import("./marketplace.js").resolveMarketplaceInstallShortcut;
|
||||
|
||||
vi.mock("./install.js", () => ({
|
||||
installPluginFromPath: (...args: unknown[]) => installPluginFromPathMock(...args),
|
||||
|
|
@ -38,6 +41,11 @@ vi.mock("../process/exec.js", () => ({
|
|||
runCommandWithTimeout: (...args: unknown[]) => runCommandWithTimeoutMock(...args),
|
||||
}));
|
||||
|
||||
beforeAll(async () => {
|
||||
({ installPluginFromMarketplace, listMarketplacePlugins, resolveMarketplaceInstallShortcut } =
|
||||
await import("./marketplace.js"));
|
||||
});
|
||||
|
||||
async function withTempDir<T>(fn: (dir: string) => Promise<T>): Promise<T> {
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-marketplace-test-"));
|
||||
try {
|
||||
|
|
@ -96,7 +104,6 @@ function mockRemoteMarketplaceClone(params: { manifest: unknown; pluginDir?: str
|
|||
async function expectRemoteMarketplaceError(params: { manifest: unknown; expectedError: string }) {
|
||||
mockRemoteMarketplaceClone({ manifest: params.manifest });
|
||||
|
||||
const { listMarketplacePlugins } = await import("./marketplace.js");
|
||||
const result = await listMarketplacePlugins({ marketplace: "owner/repo" });
|
||||
|
||||
expect(result).toEqual({
|
||||
|
|
@ -188,7 +195,6 @@ describe("marketplace plugins", () => {
|
|||
],
|
||||
});
|
||||
|
||||
const { listMarketplacePlugins } = await import("./marketplace.js");
|
||||
expectMarketplaceManifestListing(await listMarketplacePlugins({ marketplace: rootDir }));
|
||||
});
|
||||
});
|
||||
|
|
@ -216,7 +222,6 @@ describe("marketplace plugins", () => {
|
|||
extensions: ["index.ts"],
|
||||
});
|
||||
|
||||
const { installPluginFromMarketplace } = await import("./marketplace.js");
|
||||
const result = await installPluginFromMarketplace({
|
||||
marketplace: manifestPath,
|
||||
plugin: "frontend-design",
|
||||
|
|
@ -248,7 +253,6 @@ describe("marketplace plugins", () => {
|
|||
}),
|
||||
);
|
||||
|
||||
const { resolveMarketplaceInstallShortcut } = await import("./marketplace.js");
|
||||
const shortcut = await withEnvAsync(
|
||||
{ HOME: homeDir, OPENCLAW_HOME: openClawHome },
|
||||
async () => await resolveMarketplaceInstallShortcut("superpowers@claude-plugins-official"),
|
||||
|
|
@ -283,7 +287,6 @@ describe("marketplace plugins", () => {
|
|||
extensions: ["index.ts"],
|
||||
});
|
||||
|
||||
const { installPluginFromMarketplace } = await import("./marketplace.js");
|
||||
const result = await installPluginFromMarketplace({
|
||||
marketplace: "owner/repo",
|
||||
plugin: "frontend-design",
|
||||
|
|
@ -307,7 +310,6 @@ describe("marketplace plugins", () => {
|
|||
],
|
||||
});
|
||||
|
||||
const { installPluginFromMarketplace } = await import("./marketplace.js");
|
||||
const result = await installPluginFromMarketplace({
|
||||
marketplace: manifestPath,
|
||||
plugin: "frontend-design",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const resolveRuntimePluginRegistryMock = vi.fn();
|
||||
const applyPluginAutoEnableMock = vi.fn();
|
||||
|
|
@ -97,8 +97,15 @@ async function expectCloseMemoryRuntimeCase(params: {
|
|||
}
|
||||
|
||||
describe("memory runtime auto-enable loading", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
beforeAll(async () => {
|
||||
({
|
||||
getActiveMemorySearchManager,
|
||||
resolveActiveMemoryBackendConfig,
|
||||
closeActiveMemorySearchManagers,
|
||||
} = await import("./memory-runtime.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
resolveRuntimePluginRegistryMock.mockReset();
|
||||
applyPluginAutoEnableMock.mockReset();
|
||||
getMemoryRuntimeMock.mockReset();
|
||||
|
|
@ -106,11 +113,6 @@ describe("memory runtime auto-enable loading", () => {
|
|||
config: params.config,
|
||||
changes: [],
|
||||
}));
|
||||
({
|
||||
getActiveMemorySearchManager,
|
||||
resolveActiveMemoryBackendConfig,
|
||||
closeActiveMemorySearchManagers,
|
||||
} = await import("./memory-runtime.js"));
|
||||
});
|
||||
|
||||
it.each([
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const loadOpenClawPluginsMock = vi.fn();
|
||||
const loadPluginManifestRegistryMock = vi.fn();
|
||||
|
|
@ -163,8 +163,12 @@ function expectBundledProviderLoad(params?: { config?: unknown; env?: NodeJS.Pro
|
|||
}
|
||||
|
||||
describe("resolvePluginProviders", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
beforeAll(async () => {
|
||||
({ resolveOwningPluginIdsForProvider } = await import("./providers.js"));
|
||||
({ resolvePluginProviders } = await import("./providers.runtime.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
loadOpenClawPluginsMock.mockReset();
|
||||
loadOpenClawPluginsMock.mockReturnValue({
|
||||
providers: [{ pluginId: "google", provider: { id: "demo-provider" } }],
|
||||
|
|
@ -187,8 +191,6 @@ describe("resolvePluginProviders", () => {
|
|||
origin: "workspace",
|
||||
}),
|
||||
]);
|
||||
({ resolveOwningPluginIdsForProvider } = await import("./providers.js"));
|
||||
({ resolvePluginProviders } = await import("./providers.runtime.js"));
|
||||
});
|
||||
|
||||
it("forwards an explicit env to plugin loading", () => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
createCompatibilityNotice,
|
||||
createCustomHook,
|
||||
|
|
@ -214,8 +214,19 @@ function expectBundleInspectState(
|
|||
}
|
||||
|
||||
describe("buildPluginStatusReport", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
beforeAll(async () => {
|
||||
({
|
||||
buildAllPluginInspectReports,
|
||||
buildPluginCompatibilityNotices,
|
||||
buildPluginCompatibilityWarnings,
|
||||
buildPluginInspectReport,
|
||||
buildPluginStatusReport,
|
||||
formatPluginCompatibilityNotice,
|
||||
summarizePluginCompatibility,
|
||||
} = await import("./status.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
loadConfigMock.mockReset();
|
||||
loadOpenClawPluginsMock.mockReset();
|
||||
applyPluginAutoEnableMock.mockReset();
|
||||
|
|
@ -235,15 +246,6 @@ describe("buildPluginStatusReport", () => {
|
|||
(params: { config: unknown }) => params.config,
|
||||
);
|
||||
setPluginLoadResult({ plugins: [] });
|
||||
({
|
||||
buildAllPluginInspectReports,
|
||||
buildPluginCompatibilityNotices,
|
||||
buildPluginCompatibilityWarnings,
|
||||
buildPluginInspectReport,
|
||||
buildPluginStatusReport,
|
||||
formatPluginCompatibilityNotice,
|
||||
summarizePluginCompatibility,
|
||||
} = await import("./status.js"));
|
||||
});
|
||||
|
||||
it("forwards an explicit env to plugin loading", () => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { importFreshModule } from "../../test/helpers/import-fresh.js";
|
||||
import { CommandLane } from "./lanes.js";
|
||||
|
||||
|
|
@ -56,8 +56,7 @@ function enqueueBlockedMainTask<T = void>(
|
|||
}
|
||||
|
||||
describe("command queue", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
beforeAll(async () => {
|
||||
({
|
||||
clearCommandLane,
|
||||
CommandLaneClearedError,
|
||||
|
|
@ -72,6 +71,9 @@ describe("command queue", () => {
|
|||
setCommandLaneConcurrency,
|
||||
waitForActiveTasks,
|
||||
} = await import("./command-queue.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
resetCommandQueueStateForTest();
|
||||
// Queue state is global across module instances, so reset main lane
|
||||
// concurrency explicitly to avoid cross-file leakage.
|
||||
|
|
@ -250,9 +252,7 @@ describe("command queue", () => {
|
|||
await blocker;
|
||||
});
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(getActiveTaskCount()).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
expect(getActiveTaskCount()).toBeGreaterThanOrEqual(1);
|
||||
|
||||
// Enqueue another task — it should be stuck behind the blocker
|
||||
let task2Ran = false;
|
||||
|
|
@ -260,9 +260,7 @@ describe("command queue", () => {
|
|||
task2Ran = true;
|
||||
});
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(getQueueSize(lane)).toBeGreaterThanOrEqual(2);
|
||||
});
|
||||
expect(getQueueSize(lane)).toBeGreaterThanOrEqual(2);
|
||||
expect(task2Ran).toBe(false);
|
||||
|
||||
// Simulate SIGUSR1: reset all lanes. Queued work (task2) should be
|
||||
|
|
@ -396,10 +394,8 @@ describe("command queue", () => {
|
|||
return "done";
|
||||
});
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(commandQueueB.getQueueSize(lane)).toBe(1);
|
||||
expect(commandQueueB.getActiveTaskCount()).toBe(1);
|
||||
});
|
||||
expect(commandQueueB.getQueueSize(lane)).toBe(1);
|
||||
expect(commandQueueB.getActiveTaskCount()).toBe(1);
|
||||
|
||||
release();
|
||||
await expect(task).resolves.toBe("done");
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ describe("runCommandWithTimeout no-output timer", () => {
|
|||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
({ runCommandWithTimeout } = await import("./exec.js"));
|
||||
spawnMock.mockClear();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const { spawnMock } = vi.hoisted(() => ({
|
||||
spawnMock: vi.fn(),
|
||||
|
|
@ -25,9 +25,11 @@ async function withPlatform<T>(platform: NodeJS.Platform, run: () => Promise<T>
|
|||
describe("killProcessTree", () => {
|
||||
let killSpy: ReturnType<typeof vi.spyOn>;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
beforeAll(async () => {
|
||||
({ killProcessTree } = await import("./kill-tree.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
spawnMock.mockClear();
|
||||
killSpy = vi.spyOn(process, "kill");
|
||||
vi.useFakeTimers();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import type { ChildProcess } from "node:child_process";
|
||||
import { EventEmitter } from "node:events";
|
||||
import { PassThrough } from "node:stream";
|
||||
import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const { spawnWithFallbackMock, killProcessTreeMock } = vi.hoisted(() => ({
|
||||
spawnWithFallbackMock: vi.fn(),
|
||||
|
|
@ -54,9 +54,11 @@ async function createAdapterHarness(params?: {
|
|||
describe("createChildAdapter", () => {
|
||||
const originalServiceMarker = process.env.OPENCLAW_SERVICE_MARKER;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
beforeAll(async () => {
|
||||
({ createChildAdapter } = await import("./child.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
spawnWithFallbackMock.mockClear();
|
||||
killProcessTreeMock.mockClear();
|
||||
delete process.env.OPENCLAW_SERVICE_MARKER;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const { spawnMock, ptyKillMock, killProcessTreeMock } = vi.hoisted(() => ({
|
||||
spawnMock: vi.fn(),
|
||||
|
|
@ -39,9 +39,11 @@ function expectSpawnEnv() {
|
|||
describe("createPtyAdapter", () => {
|
||||
let createPtyAdapter: typeof import("./pty.js").createPtyAdapter;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
beforeAll(async () => {
|
||||
({ createPtyAdapter } = await import("./pty.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
spawnMock.mockClear();
|
||||
ptyKillMock.mockClear();
|
||||
killProcessTreeMock.mockClear();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const { createPtyAdapterMock } = vi.hoisted(() => ({
|
||||
createPtyAdapterMock: vi.fn(),
|
||||
|
|
@ -35,9 +35,11 @@ function createStubPtyAdapter() {
|
|||
describe("process supervisor PTY command contract", () => {
|
||||
let createProcessSupervisor: typeof import("./supervisor.js").createProcessSupervisor;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
beforeAll(async () => {
|
||||
({ createProcessSupervisor } = await import("./supervisor.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
createPtyAdapterMock.mockClear();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
import { afterAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const selectMock = vi.hoisted(() => vi.fn());
|
||||
const createSecretsConfigIOMock = vi.hoisted(() => vi.fn());
|
||||
const readJsonObjectIfExistsMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
const mockedModuleIds = ["@clack/prompts", "./config-io.js", "./storage-scan.js"] as const;
|
||||
|
||||
vi.mock("@clack/prompts", () => ({
|
||||
confirm: vi.fn(),
|
||||
select: (...args: unknown[]) => selectMock(...args),
|
||||
|
|
@ -29,13 +27,6 @@ describe("runSecretsConfigureInteractive", () => {
|
|||
readJsonObjectIfExistsMock.mockReset();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
for (const id of mockedModuleIds) {
|
||||
vi.doUnmock(id);
|
||||
}
|
||||
vi.resetModules();
|
||||
});
|
||||
|
||||
it("does not load auth-profiles when running providers-only", async () => {
|
||||
Object.defineProperty(process.stdin, "isTTY", {
|
||||
value: true,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import type { PluginWebSearchProviderEntry } from "../plugins/types.js";
|
||||
|
||||
|
|
@ -12,11 +12,6 @@ const { resolveBundledPluginWebSearchProvidersMock } = vi.hoisted(() => ({
|
|||
resolveBundledPluginWebSearchProvidersMock: vi.fn(() => buildTestWebSearchProviders()),
|
||||
}));
|
||||
|
||||
const mockedModuleIds = [
|
||||
"../plugins/web-search-providers.js",
|
||||
"../plugins/web-search-providers.runtime.js",
|
||||
] as const;
|
||||
|
||||
let bundledWebSearchProviders: typeof import("../plugins/web-search-providers.js");
|
||||
let runtimeWebSearchProviders: typeof import("../plugins/web-search-providers.runtime.js");
|
||||
let secretResolve: typeof import("./resolve.js");
|
||||
|
|
@ -219,12 +214,6 @@ describe("runtime web tools resolution", () => {
|
|||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
for (const id of mockedModuleIds) {
|
||||
vi.doUnmock(id);
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps web search disabled when search config is absent", async () => {
|
||||
const bundledProviderSpy = vi.mocked(
|
||||
bundledWebSearchProviders.resolveBundledPluginWebSearchProviders,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { AuthProfileStore } from "../agents/auth-profiles.js";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import type { PluginWebSearchProviderEntry } from "../plugins/types.js";
|
||||
|
|
@ -13,11 +13,6 @@ const { resolveBundledPluginWebSearchProvidersMock, resolvePluginWebSearchProvid
|
|||
resolvePluginWebSearchProvidersMock: vi.fn(() => buildTestWebSearchProviders()),
|
||||
}));
|
||||
|
||||
const mockedModuleIds = [
|
||||
"../plugins/web-search-providers.js",
|
||||
"../plugins/web-search-providers.runtime.js",
|
||||
] as const;
|
||||
|
||||
let clearSecretsRuntimeSnapshot: typeof import("./runtime.js").clearSecretsRuntimeSnapshot;
|
||||
let prepareSecretsRuntimeSnapshot: typeof import("./runtime.js").prepareSecretsRuntimeSnapshot;
|
||||
|
||||
|
|
@ -261,13 +256,6 @@ describe("secrets runtime target coverage", () => {
|
|||
({ clearSecretsRuntimeSnapshot, prepareSecretsRuntimeSnapshot } = await import("./runtime.js"));
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
for (const id of mockedModuleIds) {
|
||||
vi.doUnmock(id);
|
||||
}
|
||||
vi.resetModules();
|
||||
});
|
||||
|
||||
it("handles every openclaw.json registry target when configured as active", async () => {
|
||||
const entries = listSecretTargetRegistryEntries().filter(
|
||||
(entry) => entry.configFile === "openclaw.json",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { AuthProfileStore } from "../agents/auth-profiles.js";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import type { PluginWebSearchProviderEntry } from "../plugins/types.js";
|
||||
|
|
@ -14,11 +14,6 @@ const { resolveBundledPluginWebSearchProvidersMock, resolvePluginWebSearchProvid
|
|||
resolvePluginWebSearchProvidersMock: vi.fn(() => buildTestWebSearchProviders()),
|
||||
}));
|
||||
|
||||
const mockedModuleIds = [
|
||||
"../plugins/web-search-providers.js",
|
||||
"../plugins/web-search-providers.runtime.js",
|
||||
] as const;
|
||||
|
||||
vi.mock("../plugins/web-search-providers.js", () => ({
|
||||
resolveBundledPluginWebSearchProviders: resolveBundledPluginWebSearchProvidersMock,
|
||||
}));
|
||||
|
|
@ -125,7 +120,6 @@ function loadAuthStoreWithProfiles(profiles: AuthProfileStore["profiles"]): Auth
|
|||
|
||||
describe("secrets runtime snapshot", () => {
|
||||
beforeAll(async () => {
|
||||
vi.resetModules();
|
||||
({ clearConfigCache, clearRuntimeConfigSnapshot } = await import("../config/config.js"));
|
||||
({
|
||||
activateSecretsRuntimeSnapshot,
|
||||
|
|
@ -150,13 +144,6 @@ describe("secrets runtime snapshot", () => {
|
|||
resolvePluginWebSearchProvidersMock.mockReset();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
for (const id of mockedModuleIds) {
|
||||
vi.doUnmock(id);
|
||||
}
|
||||
vi.resetModules();
|
||||
});
|
||||
|
||||
it("resolves env refs for config and auth profiles", async () => {
|
||||
const config = asConfig({
|
||||
agents: {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ let resolveWindowsUserPrincipal: typeof import("./windows-acl.js").resolveWindow
|
|||
let summarizeWindowsAcl: typeof import("./windows-acl.js").summarizeWindowsAcl;
|
||||
|
||||
beforeAll(async () => {
|
||||
vi.resetModules();
|
||||
({
|
||||
createIcaclsResetCommand,
|
||||
formatIcaclsResetCommand,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { withTempDir } from "../test-helpers/temp-dir.js";
|
|||
import { installInMemoryTaskAndFlowRegistryRuntime } from "../test-utils/task-flow-registry-runtime.js";
|
||||
import { createFlowRecord, getFlowById, resetFlowRegistryForTests } from "./flow-registry.js";
|
||||
import {
|
||||
cancelTaskById,
|
||||
createTaskRecord,
|
||||
findLatestTaskForSessionKey,
|
||||
findTaskByRunId,
|
||||
|
|
@ -62,22 +63,6 @@ vi.mock("../agents/subagent-control.js", () => ({
|
|||
killSubagentRunAdmin: (params: unknown) => hoisted.killSubagentRunAdminMock(params),
|
||||
}));
|
||||
|
||||
async function loadFreshTaskRegistryModulesForControlTest() {
|
||||
vi.resetModules();
|
||||
vi.doMock("./task-registry-delivery-runtime.js", () => ({
|
||||
sendMessage: hoisted.sendMessageMock,
|
||||
}));
|
||||
vi.doMock("../acp/control-plane/manager.js", () => ({
|
||||
getAcpSessionManager: () => ({
|
||||
cancelSession: hoisted.cancelSessionMock,
|
||||
}),
|
||||
}));
|
||||
vi.doMock("../agents/subagent-control.js", () => ({
|
||||
killSubagentRunAdmin: (params: unknown) => hoisted.killSubagentRunAdminMock(params),
|
||||
}));
|
||||
return await import("./task-registry.js");
|
||||
}
|
||||
|
||||
async function waitForAssertion(assertion: () => void, timeoutMs = 2_000, stepMs = 5) {
|
||||
const startedAt = Date.now();
|
||||
for (;;) {
|
||||
|
|
@ -1498,120 +1483,110 @@ describe("task-registry", () => {
|
|||
});
|
||||
|
||||
it("cancels ACP-backed tasks through the ACP session manager", async () => {
|
||||
await withTaskRegistryTempDir(async (root) => {
|
||||
const registry = await loadFreshTaskRegistryModulesForControlTest();
|
||||
await withTempDir({ prefix: "openclaw-task-registry-" }, async (root) => {
|
||||
process.env.OPENCLAW_STATE_DIR = root;
|
||||
registry.resetTaskRegistryForTests();
|
||||
try {
|
||||
hoisted.cancelSessionMock.mockResolvedValue(undefined);
|
||||
resetTaskRegistryForTests();
|
||||
hoisted.cancelSessionMock.mockResolvedValue(undefined);
|
||||
|
||||
const task = registry.createTaskRecord({
|
||||
runtime: "acp",
|
||||
requesterSessionKey: "agent:main:main",
|
||||
requesterOrigin: {
|
||||
const task = createTaskRecord({
|
||||
runtime: "acp",
|
||||
requesterSessionKey: "agent:main:main",
|
||||
requesterOrigin: {
|
||||
channel: "telegram",
|
||||
to: "telegram:123",
|
||||
},
|
||||
childSessionKey: "agent:codex:acp:child",
|
||||
runId: "run-cancel-acp",
|
||||
task: "Investigate issue",
|
||||
status: "running",
|
||||
deliveryStatus: "pending",
|
||||
});
|
||||
|
||||
const result = await cancelTaskById({
|
||||
cfg: {} as never,
|
||||
taskId: task.taskId,
|
||||
});
|
||||
|
||||
expect(hoisted.cancelSessionMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
cfg: {},
|
||||
sessionKey: "agent:codex:acp:child",
|
||||
reason: "task-cancel",
|
||||
}),
|
||||
);
|
||||
expect(result).toMatchObject({
|
||||
found: true,
|
||||
cancelled: true,
|
||||
task: expect.objectContaining({
|
||||
taskId: task.taskId,
|
||||
status: "cancelled",
|
||||
error: "Cancelled by operator.",
|
||||
}),
|
||||
});
|
||||
await waitForAssertion(() =>
|
||||
expect(hoisted.sendMessageMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
channel: "telegram",
|
||||
to: "telegram:123",
|
||||
},
|
||||
childSessionKey: "agent:codex:acp:child",
|
||||
runId: "run-cancel-acp",
|
||||
task: "Investigate issue",
|
||||
status: "running",
|
||||
deliveryStatus: "pending",
|
||||
});
|
||||
|
||||
const result = await registry.cancelTaskById({
|
||||
cfg: {} as never,
|
||||
taskId: task.taskId,
|
||||
});
|
||||
|
||||
expect(hoisted.cancelSessionMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
cfg: {},
|
||||
sessionKey: "agent:codex:acp:child",
|
||||
reason: "task-cancel",
|
||||
content: "Background task cancelled: ACP background task (run run-canc).",
|
||||
}),
|
||||
);
|
||||
expect(result).toMatchObject({
|
||||
found: true,
|
||||
cancelled: true,
|
||||
task: expect.objectContaining({
|
||||
taskId: task.taskId,
|
||||
status: "cancelled",
|
||||
error: "Cancelled by operator.",
|
||||
}),
|
||||
});
|
||||
await waitForAssertion(() =>
|
||||
expect(hoisted.sendMessageMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
channel: "telegram",
|
||||
to: "telegram:123",
|
||||
content: "Background task cancelled: ACP background task (run run-canc).",
|
||||
}),
|
||||
),
|
||||
);
|
||||
} finally {
|
||||
registry.resetTaskRegistryForTests();
|
||||
}
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("cancels subagent-backed tasks through subagent control", async () => {
|
||||
await withTaskRegistryTempDir(async (root) => {
|
||||
const registry = await loadFreshTaskRegistryModulesForControlTest();
|
||||
await withTempDir({ prefix: "openclaw-task-registry-" }, async (root) => {
|
||||
process.env.OPENCLAW_STATE_DIR = root;
|
||||
registry.resetTaskRegistryForTests();
|
||||
try {
|
||||
hoisted.killSubagentRunAdminMock.mockResolvedValue({
|
||||
found: true,
|
||||
killed: true,
|
||||
});
|
||||
resetTaskRegistryForTests();
|
||||
hoisted.killSubagentRunAdminMock.mockResolvedValue({
|
||||
found: true,
|
||||
killed: true,
|
||||
});
|
||||
|
||||
const task = registry.createTaskRecord({
|
||||
runtime: "subagent",
|
||||
requesterSessionKey: "agent:main:main",
|
||||
requesterOrigin: {
|
||||
const task = createTaskRecord({
|
||||
runtime: "subagent",
|
||||
requesterSessionKey: "agent:main:main",
|
||||
requesterOrigin: {
|
||||
channel: "telegram",
|
||||
to: "telegram:123",
|
||||
},
|
||||
childSessionKey: "agent:worker:subagent:child",
|
||||
runId: "run-cancel-subagent",
|
||||
task: "Investigate issue",
|
||||
status: "running",
|
||||
deliveryStatus: "pending",
|
||||
});
|
||||
|
||||
const result = await cancelTaskById({
|
||||
cfg: {} as never,
|
||||
taskId: task.taskId,
|
||||
});
|
||||
|
||||
expect(hoisted.killSubagentRunAdminMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
cfg: {},
|
||||
sessionKey: "agent:worker:subagent:child",
|
||||
}),
|
||||
);
|
||||
expect(result).toMatchObject({
|
||||
found: true,
|
||||
cancelled: true,
|
||||
task: expect.objectContaining({
|
||||
taskId: task.taskId,
|
||||
status: "cancelled",
|
||||
error: "Cancelled by operator.",
|
||||
}),
|
||||
});
|
||||
await waitForAssertion(() =>
|
||||
expect(hoisted.sendMessageMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
channel: "telegram",
|
||||
to: "telegram:123",
|
||||
},
|
||||
childSessionKey: "agent:worker:subagent:child",
|
||||
runId: "run-cancel-subagent",
|
||||
task: "Investigate issue",
|
||||
status: "running",
|
||||
deliveryStatus: "pending",
|
||||
});
|
||||
|
||||
const result = await registry.cancelTaskById({
|
||||
cfg: {} as never,
|
||||
taskId: task.taskId,
|
||||
});
|
||||
|
||||
expect(hoisted.killSubagentRunAdminMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
cfg: {},
|
||||
sessionKey: "agent:worker:subagent:child",
|
||||
content: "Background task cancelled: Subagent task (run run-canc).",
|
||||
}),
|
||||
);
|
||||
expect(result).toMatchObject({
|
||||
found: true,
|
||||
cancelled: true,
|
||||
task: expect.objectContaining({
|
||||
taskId: task.taskId,
|
||||
status: "cancelled",
|
||||
error: "Cancelled by operator.",
|
||||
}),
|
||||
});
|
||||
await waitForAssertion(() =>
|
||||
expect(hoisted.sendMessageMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
channel: "telegram",
|
||||
to: "telegram:123",
|
||||
content: "Background task cancelled: Subagent task (run run-canc).",
|
||||
}),
|
||||
),
|
||||
);
|
||||
} finally {
|
||||
registry.resetTaskRegistryForTests();
|
||||
}
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { createEmptyPluginRegistry } from "../plugins/registry-empty.js";
|
||||
import type { SpeechProviderPlugin } from "../plugins/types.js";
|
||||
|
|
@ -31,10 +31,7 @@ function createSpeechProvider(id: string, aliases?: string[]): SpeechProviderPlu
|
|||
}
|
||||
|
||||
describe("speech provider registry", () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
resolveRuntimePluginRegistryMock.mockReset();
|
||||
resolveRuntimePluginRegistryMock.mockReturnValue(undefined);
|
||||
beforeAll(async () => {
|
||||
({
|
||||
getSpeechProvider,
|
||||
listSpeechProviders,
|
||||
|
|
@ -43,6 +40,11 @@ describe("speech provider registry", () => {
|
|||
} = await import("./provider-registry.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
resolveRuntimePluginRegistryMock.mockReset();
|
||||
resolveRuntimePluginRegistryMock.mockReturnValue(undefined);
|
||||
});
|
||||
|
||||
afterEach(() => {});
|
||||
|
||||
it("uses active plugin speech providers without reloading plugins", () => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import type { PluginWebSearchProviderEntry } from "../plugins/types.js";
|
||||
|
||||
|
|
@ -64,15 +64,17 @@ describe("web search runtime", () => {
|
|||
let activateSecretsRuntimeSnapshot: typeof import("../secrets/runtime.js").activateSecretsRuntimeSnapshot;
|
||||
let clearSecretsRuntimeSnapshot: typeof import("../secrets/runtime.js").clearSecretsRuntimeSnapshot;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
beforeAll(async () => {
|
||||
({ runWebSearch } = await import("./runtime.js"));
|
||||
({ activateSecretsRuntimeSnapshot, clearSecretsRuntimeSnapshot } =
|
||||
await import("../secrets/runtime.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
resolveBundledPluginWebSearchProvidersMock.mockReset();
|
||||
resolveRuntimeWebSearchProvidersMock.mockReset();
|
||||
resolveBundledPluginWebSearchProvidersMock.mockReturnValue([]);
|
||||
resolveRuntimeWebSearchProvidersMock.mockReturnValue([]);
|
||||
({ runWebSearch } = await import("./runtime.js"));
|
||||
({ activateSecretsRuntimeSnapshot, clearSecretsRuntimeSnapshot } =
|
||||
await import("../secrets/runtime.js"));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
|
|
|||
|
|
@ -7,10 +7,6 @@
|
|||
"durationMs": 6483.10009765625,
|
||||
"testCount": 5
|
||||
},
|
||||
"src/infra/outbound/targets.channel-resolution.test.ts": {
|
||||
"durationMs": 5879.394287109375,
|
||||
"testCount": 2
|
||||
},
|
||||
"src/cron/service.issue-regressions.test.ts": {
|
||||
"durationMs": 4524.16552734375,
|
||||
"testCount": 39
|
||||
|
|
|
|||
Loading…
Reference in New Issue