test: tighten sessions tool assertions

This commit is contained in:
Peter Steinberger
2026-05-09 19:51:23 +01:00
parent b76df1fa8d
commit b7ca251a56

View File

@@ -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);
});
});