





















@@ -79,6 +79,33 @@ function createRecord(input: SessionBindingBindInput): SessionBindingRecord {
7979};
8080}
818182+function requireRecord(value: unknown, label: string): Record<string, unknown> {
83+if (!value || typeof value !== "object" || Array.isArray(value)) {
84+throw new Error(`expected ${label} to be a record`);
85+}
86+return value as Record<string, unknown>;
87+}
88+89+function expectRecordFields(record: Record<string, unknown>, fields: Record<string, unknown>) {
90+for (const [key, value] of Object.entries(fields)) {
91+expect(record[key]).toEqual(value);
92+}
93+}
94+95+async function expectSessionBindingError(promise: Promise<unknown>, code: string) {
96+try {
97+await promise;
98+} catch (error) {
99+expect(requireRecord(error, "session binding error").code).toBe(code);
100+return error;
101+}
102+throw new Error(`expected ${code} session binding error`);
103+}
104+105+function expectConversationFields(value: unknown, fields: Record<string, unknown>) {
106+expectRecordFields(requireRecord(value, "conversation"), fields);
107+}
108+82109describe("session binding service", () => {
83110beforeEach(() => {
84111__testing.resetSessionBindingAdaptersForTests();
@@ -107,16 +134,13 @@ describe("session binding service", () => {
107134108135expect(result.conversation.channel).toBe("demo-binding");
109136expect(result.conversation.accountId).toBe("default");
110-expect(bind).toHaveBeenCalledWith(
111-expect.objectContaining({
112-placement: "current",
113-conversation: expect.objectContaining({
114-channel: "demo-binding",
115-accountId: "default",
116-conversationId: "thread-1",
117-}),
118-}),
119-);
137+const bindInput = requireRecord(bind.mock.calls[0]?.[0], "bind input");
138+expect(bindInput.placement).toBe("current");
139+expectConversationFields(bindInput.conversation, {
140+channel: "demo-binding",
141+accountId: "default",
142+conversationId: "thread-1",
143+});
120144});
121145122146it("supports explicit child placement when adapter advertises it", async () => {
@@ -144,7 +168,7 @@ describe("session binding service", () => {
144168});
145169146170it("returns structured errors when adapter is unavailable", async () => {
147-await expect(
171+await expectSessionBindingError(
148172getSessionBindingService().bind({
149173targetSessionKey: "agent:main:subagent:child-1",
150174targetKind: "subagent",
@@ -154,9 +178,8 @@ describe("session binding service", () => {
154178conversationId: "thread-1",
155179},
156180}),
157-).rejects.toMatchObject({
158-code: "BINDING_ADAPTER_UNAVAILABLE",
159-});
181+"BINDING_ADAPTER_UNAVAILABLE",
182+);
160183});
161184162185it("returns structured errors for unsupported placement", async () => {
@@ -183,11 +206,11 @@ describe("session binding service", () => {
183206.catch((error) => error);
184207185208expect(isSessionBindingError(rejected)).toBe(true);
186-expect(rejected).toMatchObject({
209+expectRecordFields(requireRecord(rejected, "session binding error"), {
187210code: "BINDING_CAPABILITY_UNSUPPORTED",
188- details: {
189- placement: "child",
190-},
211+});
212+expectRecordFields(requireRecord(rejected.details, "session binding details"), {
213+placement: "child",
191214});
192215});
193216@@ -200,7 +223,7 @@ describe("session binding service", () => {
200223resolveByConversation: () => null,
201224});
202225203-await expect(
226+await expectSessionBindingError(
204227getSessionBindingService().bind({
205228targetSessionKey: "agent:main:subagent:child-1",
206229targetKind: "subagent",
@@ -210,9 +233,8 @@ describe("session binding service", () => {
210233conversationId: "thread-1",
211234},
212235}),
213-).rejects.toMatchObject({
214-code: "BINDING_CREATE_FAILED",
215-});
236+"BINDING_CREATE_FAILED",
237+);
216238});
217239218240it("reports adapter capabilities for command preflight messaging", () => {
@@ -280,56 +302,54 @@ describe("session binding service", () => {
280302ttlMs: 60_000,
281303});
282304283-expect(bound).toMatchObject({
305+expectRecordFields(requireRecord(bound, "bound record"), {
284306bindingId: "generic:workspace\u241fdefault\u241f\u241fuser:U123",
285307targetSessionKey: "agent:codex:acp:workspace-dm",
286308targetKind: "session",
287-conversation: {
288-channel: "workspace",
289-accountId: "default",
290-conversationId: "user:U123",
291-},
292309status: "active",
293-metadata: expect.objectContaining({
294-label: "workspace-dm",
295-}),
310+});
311+expectConversationFields(bound.conversation, {
312+channel: "workspace",
313+accountId: "default",
314+conversationId: "user:U123",
315+});
316+expectRecordFields(requireRecord(bound.metadata, "metadata"), {
317+label: "workspace-dm",
296318});
297319298320const resolved = service.resolveByConversation({
299321channel: "workspace",
300322accountId: "default",
301323conversationId: "user:U123",
302324});
303-expect(resolved).toMatchObject({
325+expectRecordFields(requireRecord(resolved, "resolved binding"), {
304326bindingId: bound.bindingId,
305327targetSessionKey: "agent:codex:acp:workspace-dm",
306328});
307329expect(service.listBySession("agent:codex:acp:workspace-dm")).toEqual([resolved]);
308330309331service.touch(bound.bindingId, 1234);
310-expect(
311-service.resolveByConversation({
312-channel: "workspace",
313-accountId: "default",
314-conversationId: "user:U123",
315-})?.metadata,
316-).toEqual(
317-expect.objectContaining({
332+expectRecordFields(
333+requireRecord(
334+service.resolveByConversation({
335+channel: "workspace",
336+accountId: "default",
337+conversationId: "user:U123",
338+})?.metadata,
339+"touched metadata",
340+),
341+{
318342label: "workspace-dm",
319343lastActivityAt: 1234,
320-}),
344+},
321345);
322346323-await expect(
324-service.unbind({
325-targetSessionKey: "agent:codex:acp:workspace-dm",
326-reason: "test cleanup",
327-}),
328-).resolves.toEqual([
329-expect.objectContaining({
330-bindingId: bound.bindingId,
331-}),
332-]);
347+const unbound = await service.unbind({
348+targetSessionKey: "agent:codex:acp:workspace-dm",
349+reason: "test cleanup",
350+});
351+expect(unbound).toHaveLength(1);
352+expect(unbound[0]?.bindingId).toBe(bound.bindingId);
333353expect(
334354service.resolveByConversation({
335355channel: "workspace",
@@ -354,7 +374,7 @@ describe("session binding service", () => {
354374placements: ["current"],
355375});
356376357-await expect(
377+const rejected = await expectSessionBindingError(
358378service.bind({
359379targetSessionKey: "agent:codex:acp:teamchat-room",
360380targetKind: "session",
@@ -365,32 +385,28 @@ describe("session binding service", () => {
365385},
366386placement: "child",
367387}),
368-).rejects.toMatchObject({
369-code: "BINDING_CAPABILITY_UNSUPPORTED",
370-details: {
371-channel: "teamchat",
372-accountId: "default",
373-placement: "child",
374-},
388+"BINDING_CAPABILITY_UNSUPPORTED",
389+);
390+expectRecordFields(requireRecord(requireRecord(rejected, "rejected").details, "details"), {
391+channel: "teamchat",
392+accountId: "default",
393+placement: "child",
375394});
376395377-await expect(
378-service.bind({
379-targetSessionKey: "agent:codex:acp:teamchat-room",
380-targetKind: "session",
381-conversation: {
382-channel: "teamchat",
383-accountId: "default",
384-conversationId: "19:chatid@thread.v2",
385-},
386-}),
387-).resolves.toMatchObject({
396+const bound = await service.bind({
397+targetSessionKey: "agent:codex:acp:teamchat-room",
398+targetKind: "session",
388399conversation: {
389400channel: "teamchat",
390401accountId: "default",
391402conversationId: "19:chatid@thread.v2",
392403},
393404});
405+expectConversationFields(bound.conversation, {
406+channel: "teamchat",
407+accountId: "default",
408+conversationId: "19:chatid@thread.v2",
409+});
394410});
395411396412it("does not advertise generic plugin bindings from a stale global registry when the active channel registry is empty", async () => {
@@ -419,7 +435,7 @@ describe("session binding service", () => {
419435placements: [],
420436});
421437422-await expect(
438+await expectSessionBindingError(
423439service.bind({
424440targetSessionKey: "agent:codex:acp:external-chat",
425441targetKind: "session",
@@ -429,9 +445,8 @@ describe("session binding service", () => {
429445conversationId: "room-1",
430446},
431447}),
432-).rejects.toMatchObject({
433-code: "BINDING_ADAPTER_UNAVAILABLE",
434-});
448+"BINDING_ADAPTER_UNAVAILABLE",
449+);
435450} finally {
436451releasePinnedPluginChannelRegistry(pinnedEmptyChannelRegistry);
437452}
@@ -525,22 +540,19 @@ describe("session binding service", () => {
525540526541expect(second.__testing.getRegisteredAdapterKeys()).toEqual(["demo-binding:default"]);
527542528-await expect(
529-second.getSessionBindingService().bind({
530-targetSessionKey: "agent:main:subagent:child-1",
531-targetKind: "subagent",
532-conversation: {
533-channel: "demo-binding",
534-accountId: "default",
535-conversationId: "thread-1",
536-},
537-}),
538-).resolves.toMatchObject({
539-conversation: expect.objectContaining({
543+const secondBound = await second.getSessionBindingService().bind({
544+targetSessionKey: "agent:main:subagent:child-1",
545+targetKind: "subagent",
546+conversation: {
540547channel: "demo-binding",
541548accountId: "default",
542549conversationId: "thread-1",
543-}),
550+},
551+});
552+expectConversationFields(secondBound.conversation, {
553+channel: "demo-binding",
554+accountId: "default",
555+conversationId: "thread-1",
544556});
545557expect(firstBind).not.toHaveBeenCalled();
546558expect(secondBind).toHaveBeenCalledTimes(1);
@@ -551,22 +563,19 @@ describe("session binding service", () => {
551563adapter: secondAdapter,
552564});
553565554-await expect(
555-second.getSessionBindingService().bind({
556-targetSessionKey: "agent:main:subagent:child-2",
557-targetKind: "subagent",
558-conversation: {
559-channel: "demo-binding",
560-accountId: "default",
561-conversationId: "thread-2",
562-},
563-}),
564-).resolves.toMatchObject({
565-conversation: expect.objectContaining({
566+const firstBound = await second.getSessionBindingService().bind({
567+targetSessionKey: "agent:main:subagent:child-2",
568+targetKind: "subagent",
569+conversation: {
566570channel: "demo-binding",
567571accountId: "default",
568572conversationId: "thread-2",
569-}),
573+},
574+});
575+expectConversationFields(firstBound.conversation, {
576+channel: "demo-binding",
577+accountId: "default",
578+conversationId: "thread-2",
570579});
571580expect(firstBind).toHaveBeenCalledTimes(1);
572581expect(secondBind).toHaveBeenCalledTimes(1);
@@ -577,7 +586,7 @@ describe("session binding service", () => {
577586adapter: firstAdapter,
578587});
579588580-await expect(
589+await expectSessionBindingError(
581590second.getSessionBindingService().bind({
582591targetSessionKey: "agent:main:subagent:child-3",
583592targetKind: "subagent",
@@ -587,9 +596,8 @@ describe("session binding service", () => {
587596conversationId: "thread-3",
588597},
589598}),
590-).rejects.toMatchObject({
591-code: "BINDING_ADAPTER_UNAVAILABLE",
592-});
599+"BINDING_ADAPTER_UNAVAILABLE",
600+);
593601594602first.__testing.resetSessionBindingAdaptersForTests();
595603});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。