test(gateway): move sessions_send error paths to unit tests

This commit is contained in:
Peter Steinberger
2026-02-24 01:16:36 +00:00
parent 63dcd28ae0
commit 6c43d0a08e
2 changed files with 95 additions and 69 deletions

View File

@@ -204,6 +204,47 @@ describe("sessions_send gating", () => {
callGatewayMock.mockClear();
});
it("returns an error when neither sessionKey nor label is provided", async () => {
const tool = createSessionsSendTool({
agentSessionKey: "agent:main:main",
agentChannel: "whatsapp",
});
const result = await tool.execute("call-missing-target", {
message: "hi",
timeoutSeconds: 5,
});
expect(result.details).toMatchObject({
status: "error",
error: "Either sessionKey or label is required",
});
expect(callGatewayMock).not.toHaveBeenCalled();
});
it("returns an error when label resolution fails", async () => {
callGatewayMock.mockRejectedValueOnce(new Error("No session found with label: nope"));
const tool = createSessionsSendTool({
agentSessionKey: "agent:main:main",
agentChannel: "whatsapp",
});
const result = await tool.execute("call-missing-label", {
label: "nope",
message: "hello",
timeoutSeconds: 5,
});
expect(result.details).toMatchObject({
status: "error",
});
expect((result.details as { error?: string } | undefined)?.error ?? "").toContain(
"No session found with label",
);
expect(callGatewayMock).toHaveBeenCalledTimes(1);
expect(callGatewayMock.mock.calls[0]?.[0]).toMatchObject({ method: "sessions.resolve" });
});
it("blocks cross-agent sends when tools.agentToAgent.enabled is false", async () => {
const tool = createSessionsSendTool({
agentSessionKey: "agent:main:main",

View File

@@ -22,13 +22,19 @@ const gatewayToken = "test-token";
let envSnapshot: ReturnType<typeof captureEnv>;
type SessionSendTool = ReturnType<typeof createOpenClawTools>[number];
const SESSION_SEND_E2E_TIMEOUT_MS = 10_000;
let cachedSessionsSendTool: SessionSendTool | null = null;
function getSessionsSendTool(): SessionSendTool {
if (cachedSessionsSendTool) {
return cachedSessionsSendTool;
}
const tool = createOpenClawTools().find((candidate) => candidate.name === "sessions_send");
if (!tool) {
throw new Error("missing sessions_send tool");
}
return tool;
cachedSessionsSendTool = tool;
return cachedSessionsSendTool;
}
async function emitLifecycleAssistantReply(params: {
@@ -145,7 +151,10 @@ describe("sessions_send gateway loopback", () => {
});
describe("sessions_send label lookup", () => {
it("finds session by label and sends message", { timeout: 60_000 }, async () => {
it(
"finds session by label and sends message",
{ timeout: SESSION_SEND_E2E_TIMEOUT_MS },
async () => {
// This is an operator feature; enable broader session tool targeting for this test.
const configPath = process.env.OPENCLAW_CONFIG_PATH;
if (!configPath) {
@@ -191,30 +200,6 @@ describe("sessions_send label lookup", () => {
expect(details.status).toBe("ok");
expect(details.reply).toBe("labeled response");
expect(details.sessionKey).toBe("agent:main:test-labeled-session");
});
it("returns error when label not found", { timeout: 60_000 }, async () => {
const tool = getSessionsSendTool();
const result = await tool.execute("call-missing-label", {
label: "nonexistent-label",
message: "hello",
timeoutSeconds: 5,
});
const details = result.details as { status?: string; error?: string };
expect(details.status).toBe("error");
expect(details.error).toContain("No session found with label");
});
it("returns error when neither sessionKey nor label provided", { timeout: 60_000 }, async () => {
const tool = getSessionsSendTool();
const result = await tool.execute("call-no-key", {
message: "hello",
timeoutSeconds: 5,
});
const details = result.details as { status?: string; error?: string };
expect(details.status).toBe("error");
expect(details.error).toContain("Either sessionKey or label is required");
});
},
);
});