refactor(test): dedupe channel and monitor action suites

This commit is contained in:
Peter Steinberger
2026-02-18 04:48:51 +00:00
parent 31f83c86b2
commit a69e7682c1
11 changed files with 506 additions and 789 deletions

View File

@@ -79,24 +79,33 @@ vi.mock("../infra/provider-usage.js", () => ({
import "./test-helpers/fast-core-tools.js";
import { createOpenClawTools } from "./openclaw-tools.js";
function resetSessionStore(store: Record<string, unknown>) {
loadSessionStoreMock.mockReset();
updateSessionStoreMock.mockReset();
loadSessionStoreMock.mockReturnValue(store);
}
function getSessionStatusTool(agentSessionKey = "main") {
const tool = createOpenClawTools({ agentSessionKey }).find(
(candidate) => candidate.name === "session_status",
);
expect(tool).toBeDefined();
if (!tool) {
throw new Error("missing session_status tool");
}
return tool;
}
describe("session_status tool", () => {
it("returns a status card for the current session", async () => {
loadSessionStoreMock.mockReset();
updateSessionStoreMock.mockReset();
loadSessionStoreMock.mockReturnValue({
resetSessionStore({
main: {
sessionId: "s1",
updatedAt: 10,
},
});
const tool = createOpenClawTools({ agentSessionKey: "main" }).find(
(candidate) => candidate.name === "session_status",
);
expect(tool).toBeDefined();
if (!tool) {
throw new Error("missing session_status tool");
}
const tool = getSessionStatusTool();
const result = await tool.execute("call1", {});
const details = result.details as { ok?: boolean; statusText?: string };
@@ -107,19 +116,11 @@ describe("session_status tool", () => {
});
it("errors for unknown session keys", async () => {
loadSessionStoreMock.mockReset();
updateSessionStoreMock.mockReset();
loadSessionStoreMock.mockReturnValue({
resetSessionStore({
main: { sessionId: "s1", updatedAt: 10 },
});
const tool = createOpenClawTools({ agentSessionKey: "main" }).find(
(candidate) => candidate.name === "session_status",
);
expect(tool).toBeDefined();
if (!tool) {
throw new Error("missing session_status tool");
}
const tool = getSessionStatusTool();
await expect(tool.execute("call2", { sessionKey: "nope" })).rejects.toThrow(
"Unknown sessionId",
@@ -128,23 +129,15 @@ describe("session_status tool", () => {
});
it("resolves sessionId inputs", async () => {
loadSessionStoreMock.mockReset();
updateSessionStoreMock.mockReset();
const sessionId = "sess-main";
loadSessionStoreMock.mockReturnValue({
resetSessionStore({
"agent:main:main": {
sessionId,
updatedAt: 10,
},
});
const tool = createOpenClawTools({ agentSessionKey: "main" }).find(
(candidate) => candidate.name === "session_status",
);
expect(tool).toBeDefined();
if (!tool) {
throw new Error("missing session_status tool");
}
const tool = getSessionStatusTool();
const result = await tool.execute("call3", { sessionKey: sessionId });
const details = result.details as { ok?: boolean; sessionKey?: string };
@@ -153,22 +146,14 @@ describe("session_status tool", () => {
});
it("uses non-standard session keys without sessionId resolution", async () => {
loadSessionStoreMock.mockReset();
updateSessionStoreMock.mockReset();
loadSessionStoreMock.mockReturnValue({
resetSessionStore({
"temp:slug-generator": {
sessionId: "sess-temp",
updatedAt: 10,
},
});
const tool = createOpenClawTools({ agentSessionKey: "main" }).find(
(candidate) => candidate.name === "session_status",
);
expect(tool).toBeDefined();
if (!tool) {
throw new Error("missing session_status tool");
}
const tool = getSessionStatusTool();
const result = await tool.execute("call4", { sessionKey: "temp:slug-generator" });
const details = result.details as { ok?: boolean; sessionKey?: string };
@@ -177,22 +162,14 @@ describe("session_status tool", () => {
});
it("blocks cross-agent session_status without agent-to-agent access", async () => {
loadSessionStoreMock.mockReset();
updateSessionStoreMock.mockReset();
loadSessionStoreMock.mockReturnValue({
resetSessionStore({
"agent:other:main": {
sessionId: "s2",
updatedAt: 10,
},
});
const tool = createOpenClawTools({ agentSessionKey: "agent:main:main" }).find(
(candidate) => candidate.name === "session_status",
);
expect(tool).toBeDefined();
if (!tool) {
throw new Error("missing session_status tool");
}
const tool = getSessionStatusTool("agent:main:main");
await expect(tool.execute("call5", { sessionKey: "agent:other:main" })).rejects.toThrow(
"Agent-to-agent status is disabled",
@@ -228,13 +205,7 @@ describe("session_status tool", () => {
},
);
const tool = createOpenClawTools({ agentSessionKey: "agent:support:main" }).find(
(candidate) => candidate.name === "session_status",
);
expect(tool).toBeDefined();
if (!tool) {
throw new Error("missing session_status tool");
}
const tool = getSessionStatusTool("agent:support:main");
const result = await tool.execute("call6", { sessionKey: "main" });
const details = result.details as { ok?: boolean; sessionKey?: string };
@@ -243,9 +214,7 @@ describe("session_status tool", () => {
});
it("resets per-session model override via model=default", async () => {
loadSessionStoreMock.mockReset();
updateSessionStoreMock.mockReset();
loadSessionStoreMock.mockReturnValue({
resetSessionStore({
main: {
sessionId: "s1",
updatedAt: 10,
@@ -255,13 +224,7 @@ describe("session_status tool", () => {
},
});
const tool = createOpenClawTools({ agentSessionKey: "main" }).find(
(candidate) => candidate.name === "session_status",
);
expect(tool).toBeDefined();
if (!tool) {
throw new Error("missing session_status tool");
}
const tool = getSessionStatusTool();
await tool.execute("call3", { model: "default" });
expect(updateSessionStoreMock).toHaveBeenCalled();

View File

@@ -5,60 +5,55 @@ import { handleDiscordMessagingAction } from "./discord-actions-messaging.js";
import { handleDiscordModerationAction } from "./discord-actions-moderation.js";
import { handleDiscordAction } from "./discord-actions.js";
const createChannelDiscord = vi.fn(async () => ({
id: "new-channel",
name: "test",
type: 0,
const discordSendMocks = vi.hoisted(() => ({
banMemberDiscord: vi.fn(async () => ({})),
createChannelDiscord: vi.fn(async () => ({
id: "new-channel",
name: "test",
type: 0,
})),
createThreadDiscord: vi.fn(async () => ({})),
deleteChannelDiscord: vi.fn(async () => ({ ok: true, channelId: "C1" })),
deleteMessageDiscord: vi.fn(async () => ({})),
editChannelDiscord: vi.fn(async () => ({
id: "C1",
name: "edited",
})),
editMessageDiscord: vi.fn(async () => ({})),
fetchChannelPermissionsDiscord: vi.fn(async () => ({})),
fetchMessageDiscord: vi.fn(async () => ({})),
fetchReactionsDiscord: vi.fn(async () => ({})),
kickMemberDiscord: vi.fn(async () => ({})),
listGuildChannelsDiscord: vi.fn(async () => []),
listPinsDiscord: vi.fn(async () => ({})),
listThreadsDiscord: vi.fn(async () => ({})),
moveChannelDiscord: vi.fn(async () => ({ ok: true })),
pinMessageDiscord: vi.fn(async () => ({})),
reactMessageDiscord: vi.fn(async () => ({})),
readMessagesDiscord: vi.fn(async () => []),
removeChannelPermissionDiscord: vi.fn(async () => ({ ok: true })),
removeOwnReactionsDiscord: vi.fn(async () => ({ removed: ["👍"] })),
removeReactionDiscord: vi.fn(async () => ({})),
searchMessagesDiscord: vi.fn(async () => ({})),
sendMessageDiscord: vi.fn(async () => ({})),
sendPollDiscord: vi.fn(async () => ({})),
sendStickerDiscord: vi.fn(async () => ({})),
sendVoiceMessageDiscord: vi.fn(async () => ({})),
setChannelPermissionDiscord: vi.fn(async () => ({ ok: true })),
timeoutMemberDiscord: vi.fn(async () => ({})),
unpinMessageDiscord: vi.fn(async () => ({})),
}));
const createThreadDiscord = vi.fn(async () => ({}));
const deleteChannelDiscord = vi.fn(async () => ({ ok: true, channelId: "C1" }));
const deleteMessageDiscord = vi.fn(async () => ({}));
const editChannelDiscord = vi.fn(async () => ({
id: "C1",
name: "edited",
}));
const editMessageDiscord = vi.fn(async () => ({}));
const fetchMessageDiscord = vi.fn(async () => ({}));
const fetchChannelPermissionsDiscord = vi.fn(async () => ({}));
const fetchReactionsDiscord = vi.fn(async () => ({}));
const listGuildChannelsDiscord = vi.fn(async () => []);
const listPinsDiscord = vi.fn(async () => ({}));
const listThreadsDiscord = vi.fn(async () => ({}));
const moveChannelDiscord = vi.fn(async () => ({ ok: true }));
const pinMessageDiscord = vi.fn(async () => ({}));
const reactMessageDiscord = vi.fn(async () => ({}));
const readMessagesDiscord = vi.fn(async () => []);
const removeChannelPermissionDiscord = vi.fn(async () => ({ ok: true }));
const removeOwnReactionsDiscord = vi.fn(async () => ({ removed: ["👍"] }));
const removeReactionDiscord = vi.fn(async () => ({}));
const searchMessagesDiscord = vi.fn(async () => ({}));
const sendMessageDiscord = vi.fn(async () => ({}));
const sendVoiceMessageDiscord = vi.fn(async () => ({}));
const sendPollDiscord = vi.fn(async () => ({}));
const sendStickerDiscord = vi.fn(async () => ({}));
const setChannelPermissionDiscord = vi.fn(async () => ({ ok: true }));
const unpinMessageDiscord = vi.fn(async () => ({}));
const timeoutMemberDiscord = vi.fn(async () => ({}));
const kickMemberDiscord = vi.fn(async () => ({}));
const banMemberDiscord = vi.fn(async () => ({}));
vi.mock("../../discord/send.js", () => ({
banMemberDiscord,
const {
createChannelDiscord,
createThreadDiscord,
deleteChannelDiscord,
deleteMessageDiscord,
editChannelDiscord,
editMessageDiscord,
fetchMessageDiscord,
fetchChannelPermissionsDiscord,
fetchReactionsDiscord,
kickMemberDiscord,
listGuildChannelsDiscord,
listPinsDiscord,
listThreadsDiscord,
moveChannelDiscord,
pinMessageDiscord,
reactMessageDiscord,
readMessagesDiscord,
removeChannelPermissionDiscord,
@@ -67,11 +62,12 @@ vi.mock("../../discord/send.js", () => ({
searchMessagesDiscord,
sendMessageDiscord,
sendVoiceMessageDiscord,
sendPollDiscord,
sendStickerDiscord,
setChannelPermissionDiscord,
timeoutMemberDiscord,
unpinMessageDiscord,
} = discordSendMocks;
vi.mock("../../discord/send.js", () => ({
...discordSendMocks,
}));
const enableAllActions = () => true;
@@ -388,35 +384,15 @@ describe("handleDiscordGuildAction - channel management", () => {
});
});
it("clears the channel parent when parentId is null", async () => {
it.each([
["parentId is null", { parentId: null }],
["clearParent is true", { clearParent: true }],
])("clears the channel parent when %s", async (_label, payload) => {
await handleDiscordGuildAction(
"channelEdit",
{
channelId: "C1",
parentId: null,
},
channelsEnabled,
);
expect(editChannelDiscord).toHaveBeenCalledWith({
channelId: "C1",
name: undefined,
topic: undefined,
position: undefined,
parentId: null,
nsfw: undefined,
rateLimitPerUser: undefined,
archived: undefined,
locked: undefined,
autoArchiveDuration: undefined,
});
});
it("clears the channel parent when clearParent is true", async () => {
await handleDiscordGuildAction(
"channelEdit",
{
channelId: "C1",
clearParent: true,
...payload,
},
channelsEnabled,
);
@@ -458,31 +434,16 @@ describe("handleDiscordGuildAction - channel management", () => {
});
});
it("clears the channel parent on move when parentId is null", async () => {
it.each([
["parentId is null", { parentId: null }],
["clearParent is true", { clearParent: true }],
])("clears the channel parent on move when %s", async (_label, payload) => {
await handleDiscordGuildAction(
"channelMove",
{
guildId: "G1",
channelId: "C1",
parentId: null,
},
channelsEnabled,
);
expect(moveChannelDiscord).toHaveBeenCalledWith({
guildId: "G1",
channelId: "C1",
parentId: null,
position: undefined,
});
});
it("clears the channel parent on move when clearParent is true", async () => {
await handleDiscordGuildAction(
"channelMove",
{
guildId: "G1",
channelId: "C1",
clearParent: true,
...payload,
},
channelsEnabled,
);