





















@@ -22,6 +22,19 @@ import {
22222323const { createSessionStoreDir, openClient, getHarness } = setupGatewaySessionsTestHarness();
242425+function requireRecord(value: unknown): Record<string, unknown> {
26+expect(value).toBeTruthy();
27+expect(typeof value).toBe("object");
28+expect(Array.isArray(value)).toBe(false);
29+return value as Record<string, unknown>;
30+}
31+32+function requireFirstCallArg(mock: { mock: { calls: readonly (readonly unknown[])[] } }) {
33+const call = mock.mock.calls[0];
34+expect(call).toBeTruthy();
35+return call?.[0];
36+}
37+2538test("webchat clients cannot patch, delete, compact, or restore sessions", async () => {
2639const { dir } = await createSessionStoreDir();
2740const fixture = await createCheckpointFixture(dir);
@@ -127,23 +140,16 @@ test("session:patch hook fires with correct context", async () => {
127140});
128141129142expect(patched.ok).toBe(true);
130-expect(sessionHookMocks.triggerInternalHook).toHaveBeenCalledWith(
131-expect.objectContaining({
132-type: "session",
133-action: "patch",
134-sessionKey: expect.stringMatching(/agent:main:main/),
135-context: expect.objectContaining({
136-sessionEntry: expect.objectContaining({
137-sessionId: "sess-hook-test",
138-label: "updated-label",
139-}),
140-patch: expect.objectContaining({
141-label: "updated-label",
142-}),
143-cfg: expect.any(Object),
144-}),
145-}),
146-);
143+const event = requireRecord(requireFirstCallArg(sessionHookMocks.triggerInternalHook));
144+expect(event.type).toBe("session");
145+expect(event.action).toBe("patch");
146+expect(event.sessionKey).toBe("agent:main:main");
147+const context = requireRecord(event.context);
148+const sessionEntry = requireRecord(context.sessionEntry);
149+expect(sessionEntry.sessionId).toBe("sess-hook-test");
150+expect(sessionEntry.label).toBe("updated-label");
151+expect(requireRecord(context.patch).label).toBe("updated-label");
152+requireRecord(context.cfg);
147153148154ws.close();
149155});
@@ -218,12 +224,9 @@ test("session:patch hook only fires after successful patch", async () => {
218224});
219225220226expect(validPatch.ok).toBe(true);
221-expect(sessionHookMocks.triggerInternalHook).toHaveBeenCalledWith(
222-expect.objectContaining({
223-type: "session",
224-action: "patch",
225-}),
226-);
227+const event = requireRecord(requireFirstCallArg(sessionHookMocks.triggerInternalHook));
228+expect(event.type).toBe("session");
229+expect(event.action).toBe("patch");
227230228231ws.close();
229232});
@@ -239,13 +242,14 @@ test("session:patch skips clone and dispatch when no hooks listen", async () =>
239242});
240243241244expect(patched.ok).toBe(true);
242-expect(structuredCloneSpy).not.toHaveBeenCalledWith(
243-expect.objectContaining({
244-cfg: expect.any(Object),
245-patch: expect.any(Object),
246-sessionEntry: expect.any(Object),
247-}),
248-);
245+const clonedHookContexts = structuredCloneSpy.mock.calls.filter(([value]) => {
246+if (!value || typeof value !== "object" || Array.isArray(value)) {
247+return false;
248+}
249+const record = value as Record<string, unknown>;
250+return Boolean(record.cfg && record.patch && record.sessionEntry);
251+});
252+expect(clonedHookContexts).toHaveLength(0);
249253expect(sessionHookMocks.triggerInternalHook).not.toHaveBeenCalled();
250254251255structuredCloneSpy.mockRestore();
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。