test: tighten session kill response assertions

This commit is contained in:
Peter Steinberger
2026-05-11 03:57:07 +01:00
parent 96d761404a
commit 333879d924
+40 -39
View File
@@ -108,6 +108,20 @@ async function post(
});
}
function expectErrorResponse(body: unknown, expected: { type: string; message?: string }) {
const response = body as {
ok?: unknown;
error?: { type?: unknown; message?: unknown };
};
if (Object.hasOwn(response, "ok")) {
expect(response.ok).toBe(false);
}
expect(response.error?.type).toBe(expected.type);
if (expected.message !== undefined) {
expect(response.error?.message).toBe(expected.message);
}
}
describe("POST /sessions/:sessionKey/kill", () => {
it("returns 401 when auth fails", async () => {
authMock.mockResolvedValueOnce({ ok: false, rateLimited: false });
@@ -128,10 +142,7 @@ describe("POST /sessions/:sessionKey/kill", () => {
},
);
expect(response.status).toBe(404);
await expect(response.json()).resolves.toMatchObject({
ok: false,
error: { type: "not_found" },
});
expectErrorResponse(await response.json(), { type: "not_found" });
expect(killSubagentRunAdminMock).not.toHaveBeenCalled();
});
@@ -140,11 +151,9 @@ describe("POST /sessions/:sessionKey/kill", () => {
async (pathname) => {
const response = await post(pathname);
expect(response.status).toBe(400);
await expect(response.json()).resolves.toMatchObject({
error: {
message: "invalid session key",
type: "invalid_request_error",
},
expectErrorResponse(await response.json(), {
message: "invalid session key",
type: "invalid_request_error",
});
expect(authMock).not.toHaveBeenCalled();
expect(loadSessionEntryMock).not.toHaveBeenCalled();
@@ -198,12 +207,9 @@ describe("POST /sessions/:sessionKey/kill", () => {
it("rejects local bearer-auth kills without a trusted admin scope surface", async () => {
const response = await post("/sessions/agent%3Amain%3Asubagent%3Aworker/kill");
expect(response.status).toBe(403);
await expect(response.json()).resolves.toMatchObject({
ok: false,
error: {
type: "forbidden",
message: "missing scope: operator.admin",
},
expectErrorResponse(await response.json(), {
type: "forbidden",
message: "missing scope: operator.admin",
});
expect(loadSessionEntryMock).not.toHaveBeenCalled();
expect(killSubagentRunAdminMock).not.toHaveBeenCalled();
@@ -218,12 +224,9 @@ describe("POST /sessions/:sessionKey/kill", () => {
},
);
expect(response.status).toBe(403);
await expect(response.json()).resolves.toMatchObject({
ok: false,
error: {
type: "forbidden",
message: "missing scope: operator.admin",
},
expectErrorResponse(await response.json(), {
type: "forbidden",
message: "missing scope: operator.admin",
});
expect(loadSessionEntryMock).not.toHaveBeenCalled();
expect(killSubagentRunAdminMock).not.toHaveBeenCalled();
@@ -238,10 +241,7 @@ describe("POST /sessions/:sessionKey/kill", () => {
const response = await post("/sessions/agent%3Amain%3Asubagent%3Aworker/kill");
expect(response.status).toBe(403);
await expect(response.json()).resolves.toMatchObject({
ok: false,
error: { type: "forbidden" },
});
expectErrorResponse(await response.json(), { type: "forbidden" });
expect(killSubagentRunAdminMock).not.toHaveBeenCalled();
});
@@ -309,14 +309,18 @@ describe("POST /sessions/:sessionKey/kill", () => {
});
expect(response.status).toBe(200);
await expect(response.json()).resolves.toEqual({ ok: true, killed: false });
expect(killControlledSubagentRunMock).toHaveBeenCalledWith({
cfg,
controller: { controllerSessionKey: "agent:main:main" },
entry: expect.objectContaining({
runId: "run-current-ended",
childSessionKey: "agent:main:subagent:worker",
}),
});
expect(killControlledSubagentRunMock).toHaveBeenCalledTimes(1);
const killCall = killControlledSubagentRunMock.mock.calls[0]?.[0] as
| {
cfg?: unknown;
controller?: { controllerSessionKey?: string };
entry?: { runId?: string; childSessionKey?: string };
}
| undefined;
expect(killCall?.cfg).toBe(cfg);
expect(killCall?.controller?.controllerSessionKey).toBe("agent:main:main");
expect(killCall?.entry?.runId).toBe("run-current-ended");
expect(killCall?.entry?.childSessionKey).toBe("agent:main:subagent:worker");
});
it("rejects bearer-auth requester kills without a trusted write scope surface", async () => {
@@ -327,12 +331,9 @@ describe("POST /sessions/:sessionKey/kill", () => {
{ "x-openclaw-requester-session-key": "agent:other:main" },
);
expect(response.status).toBe(403);
await expect(response.json()).resolves.toMatchObject({
ok: false,
error: {
type: "forbidden",
message: "missing scope: operator.write",
},
expectErrorResponse(await response.json(), {
type: "forbidden",
message: "missing scope: operator.write",
});
expect(loadSessionEntryMock).not.toHaveBeenCalled();
expect(killSubagentRunAdminMock).not.toHaveBeenCalled();