* 'main' of https://github.com/openclaw/openclaw:
  build: update deps and fix vitest 4 regressions
This commit is contained in:
Vincent Koc
2026-03-12 21:04:02 -04:00
29 changed files with 1811 additions and 926 deletions

View File

@@ -512,13 +512,15 @@ describe("update-cli", () => {
call[0][1] === "i" &&
call[0][2] === "-g",
);
const mergedPath = updateCall?.[1]?.env?.Path ?? updateCall?.[1]?.env?.PATH ?? "";
const updateOptions =
typeof updateCall?.[1] === "object" && updateCall[1] !== null ? updateCall[1] : undefined;
const mergedPath = updateOptions?.env?.Path ?? updateOptions?.env?.PATH ?? "";
expect(mergedPath.split(path.delimiter).slice(0, 2)).toEqual([
portableGitMingw,
portableGitUsr,
]);
expect(updateCall?.[1]?.env?.NPM_CONFIG_SCRIPT_SHELL).toBe("cmd.exe");
expect(updateCall?.[1]?.env?.NODE_LLAMA_CPP_SKIP_DOWNLOAD).toBe("1");
expect(updateOptions?.env?.NPM_CONFIG_SCRIPT_SHELL).toBe("cmd.exe");
expect(updateOptions?.env?.NODE_LLAMA_CPP_SKIP_DOWNLOAD).toBe("1");
});
it("uses OPENCLAW_UPDATE_PACKAGE_SPEC for package updates", async () => {

View File

@@ -6,12 +6,14 @@ import { makeCfg, makeJob } from "./isolated-agent.test-harness.js";
export function createCliDeps(overrides: Partial<CliDeps> = {}): CliDeps {
return {
sendMessageSlack: vi.fn(),
sendMessageWhatsApp: vi.fn(),
sendMessageTelegram: vi.fn(),
sendMessageDiscord: vi.fn(),
sendMessageSignal: vi.fn(),
sendMessageIMessage: vi.fn(),
sendMessageSlack: vi.fn().mockResolvedValue({ messageTs: "slack-1", channel: "C1" }),
sendMessageWhatsApp: vi
.fn()
.mockResolvedValue({ messageId: "wa-1", toJid: "123@s.whatsapp.net" }),
sendMessageTelegram: vi.fn().mockResolvedValue({ messageId: "tg-1", chatId: "123" }),
sendMessageDiscord: vi.fn().mockResolvedValue({ messageId: "discord-1", channelId: "123" }),
sendMessageSignal: vi.fn().mockResolvedValue({ messageId: "signal-1", conversationId: "123" }),
sendMessageIMessage: vi.fn().mockResolvedValue({ messageId: "imessage-1", chatId: "123" }),
...overrides,
};
}

View File

@@ -1,5 +1,5 @@
import "./isolated-agent.mocks.js";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { beforeEach, describe, expect, it } from "vitest";
import { runSubagentAnnounceFlow } from "../agents/subagent-announce.js";
import {
createCliDeps,
@@ -15,7 +15,7 @@ describe("runCronIsolatedAgentTurn forum topic delivery", () => {
setupIsolatedAgentTurnMocks();
});
it("routes forum-topic and plain telegram targets through the correct delivery path", async () => {
it("routes forum-topic telegram targets through the correct delivery path", async () => {
await withTempCronHome(async (home) => {
const storePath = await writeSessionStore(home, { lastProvider: "webchat", lastTo: "" });
const deps = createCliDeps();
@@ -36,8 +36,13 @@ describe("runCronIsolatedAgentTurn forum topic delivery", () => {
text: "forum message",
messageThreadId: 42,
});
});
});
vi.clearAllMocks();
it("routes plain telegram targets through the correct delivery path", async () => {
await withTempCronHome(async (home) => {
const storePath = await writeSessionStore(home, { lastProvider: "webchat", lastTo: "" });
const deps = createCliDeps();
mockAgentPayloads([{ text: "plain message" }]);
const plainRes = await runTelegramAnnounceTurn({

View File

@@ -197,7 +197,7 @@ describe("runCronIsolatedAgentTurn", () => {
setupIsolatedAgentTurnMocks();
});
it("delivers explicit targets with direct and final-payload text", async () => {
it("delivers explicit targets directly", async () => {
await withTelegramAnnounceFixture(async ({ home, storePath, deps }) => {
await assertExplicitTelegramTargetDelivery({
home,
@@ -206,7 +206,11 @@ describe("runCronIsolatedAgentTurn", () => {
payloads: [{ text: "hello from cron" }],
expectedText: "hello from cron",
});
vi.clearAllMocks();
});
});
it("delivers explicit targets with final payload text", async () => {
await withTelegramAnnounceFixture(async ({ home, storePath, deps }) => {
await assertExplicitTelegramTargetDelivery({
home,
storePath,

View File

@@ -46,31 +46,51 @@ export const pickLastNonEmptyTextFromPayloadsMock = createMock();
export const resolveCronDeliveryPlanMock = createMock();
export const resolveDeliveryTargetMock = createMock();
vi.mock("../../agents/agent-scope.js", () => ({
resolveAgentConfig: resolveAgentConfigMock,
resolveAgentDir: vi.fn().mockReturnValue("/tmp/agent-dir"),
resolveAgentModelFallbacksOverride: resolveAgentModelFallbacksOverrideMock,
resolveAgentWorkspaceDir: vi.fn().mockReturnValue("/tmp/workspace"),
resolveDefaultAgentId: vi.fn().mockReturnValue("default"),
resolveAgentSkillsFilter: resolveAgentSkillsFilterMock,
}));
vi.mock("../../agents/agent-scope.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../agents/agent-scope.js")>();
return {
...actual,
resolveAgentConfig: resolveAgentConfigMock,
resolveAgentDir: vi.fn().mockReturnValue("/tmp/agent-dir"),
resolveAgentModelFallbacksOverride: resolveAgentModelFallbacksOverrideMock,
resolveAgentWorkspaceDir: vi.fn().mockReturnValue("/tmp/workspace"),
resolveDefaultAgentId: vi.fn().mockReturnValue("default"),
resolveAgentSkillsFilter: resolveAgentSkillsFilterMock,
};
});
vi.mock("../../agents/skills.js", () => ({
buildWorkspaceSkillSnapshot: buildWorkspaceSkillSnapshotMock,
}));
vi.mock("../../agents/skills.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../agents/skills.js")>();
return {
...actual,
buildWorkspaceSkillSnapshot: buildWorkspaceSkillSnapshotMock,
};
});
vi.mock("../../agents/skills/refresh.js", () => ({
getSkillsSnapshotVersion: vi.fn().mockReturnValue(42),
}));
vi.mock("../../agents/skills/refresh.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../agents/skills/refresh.js")>();
return {
...actual,
getSkillsSnapshotVersion: vi.fn().mockReturnValue(42),
};
});
vi.mock("../../agents/workspace.js", () => ({
DEFAULT_IDENTITY_FILENAME: "IDENTITY.md",
ensureAgentWorkspace: vi.fn().mockResolvedValue({ dir: "/tmp/workspace" }),
}));
vi.mock("../../agents/workspace.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../agents/workspace.js")>();
return {
...actual,
DEFAULT_IDENTITY_FILENAME: "IDENTITY.md",
ensureAgentWorkspace: vi.fn().mockResolvedValue({ dir: "/tmp/workspace" }),
};
});
vi.mock("../../agents/model-catalog.js", () => ({
loadModelCatalog: vi.fn().mockResolvedValue({ models: [] }),
}));
vi.mock("../../agents/model-catalog.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../agents/model-catalog.js")>();
return {
...actual,
loadModelCatalog: vi.fn().mockResolvedValue({ models: [] }),
};
});
vi.mock("../../agents/model-selection.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../agents/model-selection.js")>();
@@ -85,67 +105,119 @@ vi.mock("../../agents/model-selection.js", async (importOriginal) => {
};
});
vi.mock("../../agents/model-fallback.js", () => ({
runWithModelFallback: runWithModelFallbackMock,
}));
vi.mock("../../agents/model-fallback.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../agents/model-fallback.js")>();
return {
...actual,
runWithModelFallback: runWithModelFallbackMock,
};
});
vi.mock("../../agents/pi-embedded.js", () => ({
runEmbeddedPiAgent: runEmbeddedPiAgentMock,
}));
vi.mock("../../agents/pi-embedded.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../agents/pi-embedded.js")>();
return {
...actual,
runEmbeddedPiAgent: runEmbeddedPiAgentMock,
};
});
vi.mock("../../agents/context.js", () => ({
lookupContextTokens: vi.fn().mockReturnValue(128000),
}));
vi.mock("../../agents/context.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../agents/context.js")>();
return {
...actual,
lookupContextTokens: vi.fn().mockReturnValue(128000),
};
});
vi.mock("../../agents/date-time.js", () => ({
formatUserTime: vi.fn().mockReturnValue("2026-02-10 12:00"),
resolveUserTimeFormat: vi.fn().mockReturnValue("24h"),
resolveUserTimezone: vi.fn().mockReturnValue("UTC"),
}));
vi.mock("../../agents/date-time.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../agents/date-time.js")>();
return {
...actual,
formatUserTime: vi.fn().mockReturnValue("2026-02-10 12:00"),
resolveUserTimeFormat: vi.fn().mockReturnValue("24h"),
resolveUserTimezone: vi.fn().mockReturnValue("UTC"),
};
});
vi.mock("../../agents/timeout.js", () => ({
resolveAgentTimeoutMs: vi.fn().mockReturnValue(60_000),
}));
vi.mock("../../agents/timeout.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../agents/timeout.js")>();
return {
...actual,
resolveAgentTimeoutMs: vi.fn().mockReturnValue(60_000),
};
});
vi.mock("../../agents/usage.js", () => ({
deriveSessionTotalTokens: vi.fn().mockReturnValue(30),
hasNonzeroUsage: vi.fn().mockReturnValue(false),
}));
vi.mock("../../agents/usage.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../agents/usage.js")>();
return {
...actual,
deriveSessionTotalTokens: vi.fn().mockReturnValue(30),
hasNonzeroUsage: vi.fn().mockReturnValue(false),
};
});
vi.mock("../../agents/subagent-announce.js", () => ({
runSubagentAnnounceFlow: vi.fn().mockResolvedValue(true),
}));
vi.mock("../../agents/subagent-announce.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../agents/subagent-announce.js")>();
return {
...actual,
runSubagentAnnounceFlow: vi.fn().mockResolvedValue(true),
};
});
vi.mock("../../agents/subagent-registry.js", () => ({
countActiveDescendantRuns: countActiveDescendantRunsMock,
listDescendantRunsForRequester: listDescendantRunsForRequesterMock,
}));
vi.mock("../../agents/subagent-registry.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../agents/subagent-registry.js")>();
return {
...actual,
countActiveDescendantRuns: countActiveDescendantRunsMock,
listDescendantRunsForRequester: listDescendantRunsForRequesterMock,
};
});
vi.mock("../../agents/cli-runner.js", () => ({
runCliAgent: runCliAgentMock,
}));
vi.mock("../../agents/cli-runner.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../agents/cli-runner.js")>();
return {
...actual,
runCliAgent: runCliAgentMock,
};
});
vi.mock("../../agents/cli-session.js", () => ({
getCliSessionId: getCliSessionIdMock,
setCliSessionId: vi.fn(),
}));
vi.mock("../../agents/cli-session.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../agents/cli-session.js")>();
return {
...actual,
getCliSessionId: getCliSessionIdMock,
setCliSessionId: vi.fn(),
};
});
vi.mock("../../auto-reply/thinking.js", () => ({
normalizeThinkLevel: vi.fn().mockReturnValue(undefined),
normalizeVerboseLevel: vi.fn().mockReturnValue("off"),
supportsXHighThinking: vi.fn().mockReturnValue(false),
}));
vi.mock("../../auto-reply/thinking.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../auto-reply/thinking.js")>();
return {
...actual,
normalizeThinkLevel: vi.fn().mockReturnValue(undefined),
normalizeVerboseLevel: vi.fn().mockReturnValue("off"),
supportsXHighThinking: vi.fn().mockReturnValue(false),
};
});
vi.mock("../../cli/outbound-send-deps.js", () => ({
createOutboundSendDeps: vi.fn().mockReturnValue({}),
}));
vi.mock("../../cli/outbound-send-deps.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../cli/outbound-send-deps.js")>();
return {
...actual,
createOutboundSendDeps: vi.fn().mockReturnValue({}),
};
});
vi.mock("../../config/sessions.js", () => ({
resolveAgentMainSessionKey: vi.fn().mockReturnValue("main:default"),
resolveSessionTranscriptPath: vi.fn().mockReturnValue("/tmp/transcript.jsonl"),
setSessionRuntimeModel: vi.fn(),
updateSessionStore: updateSessionStoreMock,
}));
vi.mock("../../config/sessions.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../config/sessions.js")>();
return {
...actual,
resolveAgentMainSessionKey: vi.fn().mockReturnValue("main:default"),
resolveSessionTranscriptPath: vi.fn().mockReturnValue("/tmp/transcript.jsonl"),
setSessionRuntimeModel: vi.fn(),
updateSessionStore: updateSessionStoreMock,
};
});
vi.mock("../../routing/session-key.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../routing/session-key.js")>();
@@ -156,28 +228,48 @@ vi.mock("../../routing/session-key.js", async (importOriginal) => {
};
});
vi.mock("../../infra/agent-events.js", () => ({
registerAgentRunContext: vi.fn(),
}));
vi.mock("../../infra/agent-events.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../infra/agent-events.js")>();
return {
...actual,
registerAgentRunContext: vi.fn(),
};
});
vi.mock("../../infra/outbound/deliver.js", () => ({
deliverOutboundPayloads: vi.fn().mockResolvedValue(undefined),
}));
vi.mock("../../infra/outbound/deliver.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../infra/outbound/deliver.js")>();
return {
...actual,
deliverOutboundPayloads: vi.fn().mockResolvedValue(undefined),
};
});
vi.mock("../../infra/skills-remote.js", () => ({
getRemoteSkillEligibility: vi.fn().mockReturnValue({}),
}));
vi.mock("../../infra/skills-remote.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../infra/skills-remote.js")>();
return {
...actual,
getRemoteSkillEligibility: vi.fn().mockReturnValue({}),
};
});
vi.mock("../../logger.js", () => ({
logWarn: (...args: unknown[]) => logWarnMock(...args),
}));
vi.mock("../../logger.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../logger.js")>();
return {
...actual,
logWarn: (...args: unknown[]) => logWarnMock(...args),
};
});
vi.mock("../../security/external-content.js", () => ({
buildSafeExternalPrompt: vi.fn().mockReturnValue("safe prompt"),
detectSuspiciousPatterns: vi.fn().mockReturnValue([]),
getHookType: vi.fn().mockReturnValue("unknown"),
isExternalHookSession: vi.fn().mockReturnValue(false),
}));
vi.mock("../../security/external-content.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../security/external-content.js")>();
return {
...actual,
buildSafeExternalPrompt: vi.fn().mockReturnValue("safe prompt"),
detectSuspiciousPatterns: vi.fn().mockReturnValue([]),
getHookType: vi.fn().mockReturnValue("unknown"),
isExternalHookSession: vi.fn().mockReturnValue(false),
};
});
vi.mock("../delivery.js", () => ({
resolveCronDeliveryPlan: resolveCronDeliveryPlanMock,
@@ -200,11 +292,15 @@ vi.mock("./session.js", () => ({
resolveCronSession: resolveCronSessionMock,
}));
vi.mock("../../agents/defaults.js", () => ({
DEFAULT_CONTEXT_TOKENS: 128000,
DEFAULT_MODEL: "gpt-4",
DEFAULT_PROVIDER: "openai",
}));
vi.mock("../../agents/defaults.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../agents/defaults.js")>();
return {
...actual,
DEFAULT_CONTEXT_TOKENS: 128000,
DEFAULT_MODEL: "gpt-4",
DEFAULT_PROVIDER: "openai",
};
});
export function makeCronSessionEntry(overrides?: Record<string, unknown>): CronSessionEntry {
return {

View File

@@ -1,6 +1,7 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
const mocks = vi.hoisted(() => ({
getDefaultMediaLocalRoots: vi.fn(() => []),
dispatchChannelMessageAction: vi.fn(),
sendMessage: vi.fn(),
sendPoll: vi.fn(),
@@ -17,6 +18,7 @@ vi.mock("./message.js", () => ({
}));
vi.mock("../../media/local-roots.js", () => ({
getDefaultMediaLocalRoots: mocks.getDefaultMediaLocalRoots,
getAgentScopedMediaLocalRoots: mocks.getAgentScopedMediaLocalRoots,
}));
@@ -27,6 +29,7 @@ describe("executeSendAction", () => {
mocks.dispatchChannelMessageAction.mockClear();
mocks.sendMessage.mockClear();
mocks.sendPoll.mockClear();
mocks.getDefaultMediaLocalRoots.mockClear();
mocks.getAgentScopedMediaLocalRoots.mockClear();
});

View File

@@ -147,8 +147,7 @@ describe("resolveGatewayConnection", () => {
setup: () => pickPrimaryLanIPv4.mockReturnValue("192.168.1.42"),
},
])("uses loopback host when local bind is $label", async ({ bind, setup }) => {
loadConfig.mockReturnValue({ gateway: { mode: "local", bind } });
resolveGatewayPort.mockReturnValue(18800);
loadConfig.mockReturnValue({ gateway: { mode: "local", bind, port: 18800 } });
setup();
const result = await withEnvAsync({ OPENCLAW_GATEWAY_TOKEN: "env-token" }, async () => {