























@@ -108,6 +108,20 @@ async function post(
108108});
109109}
110110111+function expectErrorResponse(body: unknown, expected: { type: string; message?: string }) {
112+const response = body as {
113+ok?: unknown;
114+error?: { type?: unknown; message?: unknown };
115+};
116+if (Object.hasOwn(response, "ok")) {
117+expect(response.ok).toBe(false);
118+}
119+expect(response.error?.type).toBe(expected.type);
120+if (expected.message !== undefined) {
121+expect(response.error?.message).toBe(expected.message);
122+}
123+}
124+111125describe("POST /sessions/:sessionKey/kill", () => {
112126it("returns 401 when auth fails", async () => {
113127authMock.mockResolvedValueOnce({ ok: false, rateLimited: false });
@@ -128,10 +142,7 @@ describe("POST /sessions/:sessionKey/kill", () => {
128142},
129143);
130144expect(response.status).toBe(404);
131-await expect(response.json()).resolves.toMatchObject({
132-ok: false,
133-error: { type: "not_found" },
134-});
145+expectErrorResponse(await response.json(), { type: "not_found" });
135146expect(killSubagentRunAdminMock).not.toHaveBeenCalled();
136147});
137148@@ -140,11 +151,9 @@ describe("POST /sessions/:sessionKey/kill", () => {
140151async (pathname) => {
141152const response = await post(pathname);
142153expect(response.status).toBe(400);
143-await expect(response.json()).resolves.toMatchObject({
144-error: {
145-message: "invalid session key",
146-type: "invalid_request_error",
147-},
154+expectErrorResponse(await response.json(), {
155+message: "invalid session key",
156+type: "invalid_request_error",
148157});
149158expect(authMock).not.toHaveBeenCalled();
150159expect(loadSessionEntryMock).not.toHaveBeenCalled();
@@ -198,12 +207,9 @@ describe("POST /sessions/:sessionKey/kill", () => {
198207it("rejects local bearer-auth kills without a trusted admin scope surface", async () => {
199208const response = await post("/sessions/agent%3Amain%3Asubagent%3Aworker/kill");
200209expect(response.status).toBe(403);
201-await expect(response.json()).resolves.toMatchObject({
202-ok: false,
203-error: {
204-type: "forbidden",
205-message: "missing scope: operator.admin",
206-},
210+expectErrorResponse(await response.json(), {
211+type: "forbidden",
212+message: "missing scope: operator.admin",
207213});
208214expect(loadSessionEntryMock).not.toHaveBeenCalled();
209215expect(killSubagentRunAdminMock).not.toHaveBeenCalled();
@@ -218,12 +224,9 @@ describe("POST /sessions/:sessionKey/kill", () => {
218224},
219225);
220226expect(response.status).toBe(403);
221-await expect(response.json()).resolves.toMatchObject({
222-ok: false,
223-error: {
224-type: "forbidden",
225-message: "missing scope: operator.admin",
226-},
227+expectErrorResponse(await response.json(), {
228+type: "forbidden",
229+message: "missing scope: operator.admin",
227230});
228231expect(loadSessionEntryMock).not.toHaveBeenCalled();
229232expect(killSubagentRunAdminMock).not.toHaveBeenCalled();
@@ -238,10 +241,7 @@ describe("POST /sessions/:sessionKey/kill", () => {
238241239242const response = await post("/sessions/agent%3Amain%3Asubagent%3Aworker/kill");
240243expect(response.status).toBe(403);
241-await expect(response.json()).resolves.toMatchObject({
242-ok: false,
243-error: { type: "forbidden" },
244-});
244+expectErrorResponse(await response.json(), { type: "forbidden" });
245245expect(killSubagentRunAdminMock).not.toHaveBeenCalled();
246246});
247247@@ -309,14 +309,18 @@ describe("POST /sessions/:sessionKey/kill", () => {
309309});
310310expect(response.status).toBe(200);
311311await expect(response.json()).resolves.toEqual({ ok: true, killed: false });
312-expect(killControlledSubagentRunMock).toHaveBeenCalledWith({
313- cfg,
314-controller: { controllerSessionKey: "agent:main:main" },
315-entry: expect.objectContaining({
316-runId: "run-current-ended",
317-childSessionKey: "agent:main:subagent:worker",
318-}),
319-});
312+expect(killControlledSubagentRunMock).toHaveBeenCalledTimes(1);
313+const killCall = killControlledSubagentRunMock.mock.calls[0]?.[0] as
314+| {
315+cfg?: unknown;
316+controller?: { controllerSessionKey?: string };
317+entry?: { runId?: string; childSessionKey?: string };
318+}
319+| undefined;
320+expect(killCall?.cfg).toBe(cfg);
321+expect(killCall?.controller?.controllerSessionKey).toBe("agent:main:main");
322+expect(killCall?.entry?.runId).toBe("run-current-ended");
323+expect(killCall?.entry?.childSessionKey).toBe("agent:main:subagent:worker");
320324});
321325322326it("rejects bearer-auth requester kills without a trusted write scope surface", async () => {
@@ -327,12 +331,9 @@ describe("POST /sessions/:sessionKey/kill", () => {
327331{ "x-openclaw-requester-session-key": "agent:other:main" },
328332);
329333expect(response.status).toBe(403);
330-await expect(response.json()).resolves.toMatchObject({
331-ok: false,
332-error: {
333-type: "forbidden",
334-message: "missing scope: operator.write",
335-},
334+expectErrorResponse(await response.json(), {
335+type: "forbidden",
336+message: "missing scope: operator.write",
336337});
337338expect(loadSessionEntryMock).not.toHaveBeenCalled();
338339expect(killSubagentRunAdminMock).not.toHaveBeenCalled();
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。