mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 00:21:23 +00:00
refactor(test): dedupe channel and monitor action suites
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user