mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-12 00:30:42 +00:00
test: tighten sessions tool assertions
This commit is contained in:
@@ -56,6 +56,29 @@ type SessionsListResult = Awaited<
|
||||
ReturnType<ReturnType<typeof import("./sessions-list-tool.js").createSessionsListTool>["execute"]>
|
||||
>;
|
||||
|
||||
function requireRecord(value: unknown, label: string): Record<string, unknown> {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||
throw new Error(`expected ${label}`);
|
||||
}
|
||||
return value as Record<string, unknown>;
|
||||
}
|
||||
|
||||
function requireDetails(result: { details?: unknown }, label = "result details") {
|
||||
return requireRecord(result.details, label);
|
||||
}
|
||||
|
||||
function requireSessions(details: Record<string, unknown>) {
|
||||
const sessions = details.sessions;
|
||||
if (!Array.isArray(sessions)) {
|
||||
throw new Error("expected details.sessions");
|
||||
}
|
||||
return sessions.map((session, index) => requireRecord(session, `session ${index}`));
|
||||
}
|
||||
|
||||
function requireGatewayRequest(index = 0) {
|
||||
return requireRecord(callGatewayMock.mock.calls[index]?.[0], `gateway request ${index}`);
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
({ createSessionsListTool } = await import("./sessions-list-tool.js"));
|
||||
({ createSessionsSendTool } = await import("./sessions-send-tool.js"));
|
||||
@@ -167,7 +190,7 @@ function expectWorkerTranscriptPath(
|
||||
params: { containsPath: string; sessionId: string },
|
||||
) {
|
||||
const session = getFirstListedSession(result);
|
||||
expect(session).toMatchObject({ key: "agent:worker:main" });
|
||||
expect(session?.key).toBe("agent:worker:main");
|
||||
const transcriptPath = session?.transcriptPath ?? "";
|
||||
expect(path.normalize(transcriptPath)).toContain(path.normalize(params.containsPath));
|
||||
expect(transcriptPath).toMatch(new RegExp(`${params.sessionId}\\.jsonl$`));
|
||||
@@ -328,8 +351,7 @@ describe("resolveAnnounceTarget", () => {
|
||||
threadId: "99",
|
||||
});
|
||||
expect(callGatewayMock).toHaveBeenCalledTimes(1);
|
||||
const first = callGatewayMock.mock.calls[0]?.[0] as { method?: string } | undefined;
|
||||
expect(first).toMatchObject({ method: "sessions.list" });
|
||||
expect(requireGatewayRequest().method).toBe("sessions.list");
|
||||
});
|
||||
|
||||
it("falls back to origin provider and accountId from sessions.list when legacy route fields are absent", async () => {
|
||||
@@ -435,10 +457,9 @@ describe("sessions_list gating", () => {
|
||||
it("filters out other agents when tools.agentToAgent.enabled is false", async () => {
|
||||
const tool = createMainSessionsListTool();
|
||||
const result = await tool.execute("call1", {});
|
||||
expect(result.details).toMatchObject({
|
||||
count: 1,
|
||||
sessions: [{ key: MAIN_AGENT_SESSION_KEY }],
|
||||
});
|
||||
const details = requireDetails(result);
|
||||
expect(details.count).toBe(1);
|
||||
expect(requireSessions(details)[0]?.key).toBe(MAIN_AGENT_SESSION_KEY);
|
||||
});
|
||||
|
||||
it("keeps requester-owned cross-agent rows with tree visibility without a spawned lookup", async () => {
|
||||
@@ -462,10 +483,11 @@ describe("sessions_list gating", () => {
|
||||
|
||||
const result = await createMainSessionsListTool().execute("call1", {});
|
||||
|
||||
expect(result.details).toMatchObject({
|
||||
count: 1,
|
||||
sessions: [{ key: "agent:codex:acp:child-1", spawnedBy: MAIN_AGENT_SESSION_KEY }],
|
||||
});
|
||||
const details = requireDetails(result);
|
||||
expect(details.count).toBe(1);
|
||||
const session = requireSessions(details)[0];
|
||||
expect(session?.key).toBe("agent:codex:acp:child-1");
|
||||
expect(session?.spawnedBy).toBe(MAIN_AGENT_SESSION_KEY);
|
||||
expect(callGatewayMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
@@ -490,10 +512,11 @@ describe("sessions_list gating", () => {
|
||||
|
||||
const result = await createMainSessionsListTool().execute("call1", {});
|
||||
|
||||
expect(result.details).toMatchObject({
|
||||
count: 1,
|
||||
sessions: [{ key: "agent:codex:acp:child-1", parentSessionKey: MAIN_AGENT_SESSION_KEY }],
|
||||
});
|
||||
const details = requireDetails(result);
|
||||
expect(details.count).toBe(1);
|
||||
const session = requireSessions(details)[0];
|
||||
expect(session?.key).toBe("agent:codex:acp:child-1");
|
||||
expect(session?.parentSessionKey).toBe(MAIN_AGENT_SESSION_KEY);
|
||||
expect(callGatewayMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
@@ -653,9 +676,10 @@ describe("sessions_list channel derivation", () => {
|
||||
});
|
||||
const result = await executeMainSessionsList();
|
||||
|
||||
expect(result.details).toMatchObject({
|
||||
sessions: [{ key: "agent:main:discord:group:ops", channel: "discord" }],
|
||||
});
|
||||
const details = requireDetails(result);
|
||||
const session = requireSessions(details)[0];
|
||||
expect(session?.key).toBe("agent:main:discord:group:ops");
|
||||
expect(session?.channel).toBe("discord");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -672,10 +696,9 @@ describe("sessions_send gating", () => {
|
||||
timeoutSeconds: 5,
|
||||
});
|
||||
|
||||
expect(result.details).toMatchObject({
|
||||
status: "error",
|
||||
error: "Either sessionKey or label is required",
|
||||
});
|
||||
const details = requireDetails(result);
|
||||
expect(details.status).toBe("error");
|
||||
expect(details.error).toBe("Either sessionKey or label is required");
|
||||
expect(callGatewayMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -689,14 +712,13 @@ describe("sessions_send gating", () => {
|
||||
timeoutSeconds: 5,
|
||||
});
|
||||
|
||||
expect(result.details).toMatchObject({
|
||||
status: "error",
|
||||
});
|
||||
const details = requireDetails(result);
|
||||
expect(details.status).toBe("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" });
|
||||
expect(requireGatewayRequest().method).toBe("sessions.resolve");
|
||||
});
|
||||
|
||||
it("blocks cross-agent sends when tools.agentToAgent.enabled is false", async () => {
|
||||
@@ -709,8 +731,8 @@ describe("sessions_send gating", () => {
|
||||
});
|
||||
|
||||
expect(callGatewayMock).toHaveBeenCalledTimes(1);
|
||||
expect(callGatewayMock.mock.calls[0]?.[0]).toMatchObject({ method: "sessions.list" });
|
||||
expect(result.details).toMatchObject({ status: "forbidden" });
|
||||
expect(requireGatewayRequest().method).toBe("sessions.list");
|
||||
expect(requireDetails(result).status).toBe("forbidden");
|
||||
});
|
||||
|
||||
it("rejects direct thread session targets before dispatching an agent run", async () => {
|
||||
@@ -730,10 +752,9 @@ describe("sessions_send gating", () => {
|
||||
timeoutSeconds: 0,
|
||||
});
|
||||
|
||||
expect(result.details).toMatchObject({
|
||||
status: "error",
|
||||
sessionKey: threadSessionKey,
|
||||
});
|
||||
const details = requireDetails(result);
|
||||
expect(details.status).toBe("error");
|
||||
expect(details.sessionKey).toBe(threadSessionKey);
|
||||
expect((result.details as { error?: string } | undefined)?.error ?? "").toContain(
|
||||
"cannot target a thread session",
|
||||
);
|
||||
@@ -758,15 +779,14 @@ describe("sessions_send gating", () => {
|
||||
timeoutSeconds: 0,
|
||||
});
|
||||
|
||||
expect(result.details).toMatchObject({
|
||||
status: "error",
|
||||
sessionKey: threadSessionKey,
|
||||
});
|
||||
const details = requireDetails(result);
|
||||
expect(details.status).toBe("error");
|
||||
expect(details.sessionKey).toBe(threadSessionKey);
|
||||
expect((result.details as { error?: string } | undefined)?.error ?? "").toContain(
|
||||
"cannot target a thread session",
|
||||
);
|
||||
expect(callGatewayMock).toHaveBeenCalledTimes(1);
|
||||
expect(callGatewayMock.mock.calls[0]?.[0]).toMatchObject({ method: "sessions.resolve" });
|
||||
expect(requireGatewayRequest().method).toBe("sessions.resolve");
|
||||
});
|
||||
|
||||
it("does not reuse a stale assistant reply when no new reply appears", async () => {
|
||||
@@ -806,10 +826,9 @@ describe("sessions_send gating", () => {
|
||||
});
|
||||
|
||||
expect(historyCalls).toBe(2);
|
||||
expect(result.details).toMatchObject({
|
||||
status: "ok",
|
||||
reply: undefined,
|
||||
sessionKey: MAIN_AGENT_SESSION_KEY,
|
||||
});
|
||||
const details = requireDetails(result);
|
||||
expect(details.status).toBe("ok");
|
||||
expect(details.reply).toBeUndefined();
|
||||
expect(details.sessionKey).toBe(MAIN_AGENT_SESSION_KEY);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user