























@@ -83,6 +83,44 @@ function makeSpawnEvent(
8383};
8484}
858586+function requireRecord(value: unknown, label: string): Record<string, unknown> {
87+expect(typeof value).toBe("object");
88+expect(value).not.toBeNull();
89+if (typeof value !== "object" || value === null) {
90+throw new Error(`${label} was not an object`);
91+}
92+return value as Record<string, unknown>;
93+}
94+95+function expectRecordFields(record: Record<string, unknown>, fields: Record<string, unknown>) {
96+for (const [key, value] of Object.entries(fields)) {
97+expect(record[key]).toEqual(value);
98+}
99+}
100+101+function expectResultFields(result: unknown, fields: Record<string, unknown>) {
102+expectRecordFields(requireRecord(result, "hook result"), fields);
103+}
104+105+function expectErrorResult(result: unknown, messagePart: string) {
106+const record = requireRecord(result, "hook result");
107+expect(record.status).toBe("error");
108+expect(String(record.error).toLowerCase()).toContain(messagePart.toLowerCase());
109+}
110+111+function requireBindCallWithTarget(targetSessionKey: string) {
112+const calls = bindMock.mock.calls;
113+const call = calls.find(([params]) => {
114+const record = params as { targetSessionKey?: string };
115+return record.targetSessionKey === targetSessionKey;
116+});
117+expect(call).toBeDefined();
118+if (!call) {
119+throw new Error(`missing bind call for ${targetSessionKey}`);
120+}
121+return requireRecord(call[0], "bind params");
122+}
123+86124describe("handleMatrixSubagentSpawning", () => {
87125beforeEach(() => {
88126bindMock.mockReset();
@@ -135,7 +173,7 @@ describe("handleMatrixSubagentSpawning", () => {
135173fakeApi,
136174makeSpawnEvent({ channel: " Matrix " }),
137175);
138-expect(result).toMatchObject({ status: "ok", threadBindingReady: true });
176+expectResultFields(result, { status: "ok", threadBindingReady: true });
139177});
140178141179it("returns error when thread bindings are disabled", async () => {
@@ -151,8 +189,7 @@ describe("handleMatrixSubagentSpawning", () => {
151189} as never,
152190makeSpawnEvent(),
153191);
154-expect(result).toEqual(expect.objectContaining({ status: "error" }));
155-expect((result as { error?: string }).error).toMatch(/thread bindings are disabled/i);
192+expectErrorResult(result, "thread bindings are disabled");
156193});
157194158195it("returns error when spawnSessions is false", async () => {
@@ -168,40 +205,25 @@ describe("handleMatrixSubagentSpawning", () => {
168205} as never,
169206makeSpawnEvent(),
170207);
171-expect(result).toEqual(
172-expect.objectContaining({
173-status: "error",
174-error: expect.stringContaining("spawnSessions"),
175-}),
176-);
208+expectErrorResult(result, "spawnSessions");
177209});
178210179211it("allows thread-bound subagent spawn by default", async () => {
180212const result = await handleMatrixSubagentSpawning(fakeApi, makeSpawnEvent());
181-expect(result).toMatchObject({ status: "ok", threadBindingReady: true });
213+expectResultFields(result, { status: "ok", threadBindingReady: true });
182214});
183215184216it("returns error when requester.to has no room target", async () => {
185217const result = await handleMatrixSubagentSpawning(
186218fakeApi,
187219makeSpawnEvent({ to: "@user:example.org" }),
188220);
189-expect(result).toEqual(
190-expect.objectContaining({
191-status: "error",
192-error: expect.stringContaining("no room target"),
193-}),
194-);
221+expectErrorResult(result, "no room target");
195222});
196223197224it("returns error when requester.to is empty", async () => {
198225const result = await handleMatrixSubagentSpawning(fakeApi, makeSpawnEvent({ to: "" }));
199-expect(result).toEqual(
200-expect.objectContaining({
201-status: "error",
202-error: expect.stringContaining("no room target"),
203-}),
204-);
226+expectErrorResult(result, "no room target");
205227});
206228207229it("returns error when no binding adapter is available for the account", async () => {
@@ -212,12 +234,7 @@ describe("handleMatrixSubagentSpawning", () => {
212234unbindSupported: false,
213235});
214236const result = await handleMatrixSubagentSpawning(fakeApi, makeSpawnEvent());
215-expect(result).toEqual(
216-expect.objectContaining({
217-status: "error",
218-error: expect.stringContaining("No Matrix session binding adapter"),
219-}),
220-);
237+expectErrorResult(result, "No Matrix session binding adapter");
221238expect(bindMock).not.toHaveBeenCalled();
222239});
223240@@ -240,32 +257,33 @@ describe("handleMatrixSubagentSpawning", () => {
240257}),
241258);
242259243-expect(bindMock).toHaveBeenCalledWith(
244-expect.objectContaining({
245-targetSessionKey: "agent:ops:subagent:worker",
246-targetKind: "subagent",
247-conversation: expect.objectContaining({
248-channel: "matrix",
249-accountId: "ops",
250-conversationId: "!roomAbc:technerik.com",
251-}),
252-placement: "child",
253-metadata: expect.objectContaining({
254-agentId: "builder",
255-label: "Build Agent",
256-}),
257-}),
258-);
259-expect(result).toMatchObject({
260+const bindParams = requireBindCallWithTarget("agent:ops:subagent:worker");
261+expectRecordFields(bindParams, {
262+targetKind: "subagent",
263+placement: "child",
264+});
265+expectRecordFields(requireRecord(bindParams.conversation, "bind conversation"), {
266+channel: "matrix",
267+accountId: "ops",
268+conversationId: "!roomAbc:technerik.com",
269+});
270+expectRecordFields(requireRecord(bindParams.metadata, "bind metadata"), {
271+agentId: "builder",
272+label: "Build Agent",
273+});
274+expectResultFields(result, {
260275status: "ok",
261276threadBindingReady: true,
262-deliveryOrigin: {
277+});
278+expectRecordFields(
279+requireRecord(requireRecord(result, "hook result").deliveryOrigin, "delivery origin"),
280+{
263281channel: "matrix",
264282accountId: "ops",
265283to: "room:!roomAbc:technerik.com",
266284threadId: "$thread-ops",
267285},
268-});
286+);
269287});
270288271289it("uses 'default' as accountId when requester.accountId is absent", async () => {
@@ -281,22 +299,14 @@ describe("handleMatrixSubagentSpawning", () => {
281299channel: "matrix",
282300accountId: "default",
283301});
284-expect(bindMock).toHaveBeenCalledWith(
285-expect.objectContaining({
286-conversation: expect.objectContaining({ accountId: "default" }),
287-}),
288-);
302+const bindParams = requireBindCallWithTarget("agent:default:subagent:child");
303+expect(requireRecord(bindParams.conversation, "bind conversation").accountId).toBe("default");
289304});
290305291306it("returns error when bind() throws", async () => {
292307bindMock.mockRejectedValue(new Error("provider auth failed"));
293308const result = await handleMatrixSubagentSpawning(fakeApi, makeSpawnEvent());
294-expect(result).toEqual(
295-expect.objectContaining({
296-status: "error",
297-error: expect.stringContaining("provider auth failed"),
298-}),
299-);
309+expectErrorResult(result, "provider auth failed");
300310});
301311302312it("respects per-account threadBindings override over base config", async () => {
@@ -319,7 +329,7 @@ describe("handleMatrixSubagentSpawning", () => {
319329} as never,
320330makeSpawnEvent({ accountId: "forge" }),
321331);
322-expect(result).toMatchObject({ status: "ok", threadBindingReady: true });
332+expectResultFields(result, { status: "ok", threadBindingReady: true });
323333});
324334});
325335@@ -359,16 +369,19 @@ describe("matrix subagent hook registration", () => {
359369const result = await handler(makeSpawnEvent(), {});
360370361371expect(bindMock).toHaveBeenCalledTimes(1);
362-expect(result).toMatchObject({
372+expectResultFields(result, {
363373status: "ok",
364374threadBindingReady: true,
365-deliveryOrigin: {
375+});
376+expectRecordFields(
377+requireRecord(requireRecord(result, "hook result").deliveryOrigin, "delivery origin"),
378+{
366379channel: "matrix",
367380accountId: "default",
368381to: "room:!room123:example.org",
369382threadId: "$thread-root",
370383},
371-});
384+);
372385});
373386374387it("resolves delivery targets through the lazy registration barrel", async () => {
@@ -825,28 +838,30 @@ describe("concurrent spawns across accounts", () => {
825838spawnForAccount("forge"),
826839]);
827840828-expect(opsResult).toMatchObject({ status: "ok", threadBindingReady: true });
829-expect(forgeResult).toMatchObject({ status: "ok", threadBindingReady: true });
841+expectResultFields(opsResult, { status: "ok", threadBindingReady: true });
842+expectResultFields(forgeResult, { status: "ok", threadBindingReady: true });
830843expect(bindMock).toHaveBeenCalledTimes(2);
831844832845// Each bind call targeted the correct account's room
833-expect(bindMock).toHaveBeenCalledWith(
834-expect.objectContaining({
835-targetSessionKey: "agent:ops:subagent:child-ops",
836-conversation: expect.objectContaining({
837-accountId: "ops",
838-conversationId: "!room-ops:example.org",
839-}),
840-}),
846+expectRecordFields(
847+requireRecord(
848+requireBindCallWithTarget("agent:ops:subagent:child-ops").conversation,
849+"ops bind conversation",
850+),
851+{
852+accountId: "ops",
853+conversationId: "!room-ops:example.org",
854+},
841855);
842-expect(bindMock).toHaveBeenCalledWith(
843-expect.objectContaining({
844-targetSessionKey: "agent:forge:subagent:child-forge",
845-conversation: expect.objectContaining({
846-accountId: "forge",
847-conversationId: "!room-forge:example.org",
848-}),
849-}),
856+expectRecordFields(
857+requireRecord(
858+requireBindCallWithTarget("agent:forge:subagent:child-forge").conversation,
859+"forge bind conversation",
860+),
861+{
862+accountId: "forge",
863+conversationId: "!room-forge:example.org",
864+},
850865);
851866});
852867@@ -860,12 +875,7 @@ describe("concurrent spawns across accounts", () => {
860875spawnForAccount("forge"),
861876]);
862877863-expect(opsResult).toEqual(
864-expect.objectContaining({
865-status: "error",
866-error: expect.stringContaining("ops provider auth failed"),
867-}),
868-);
869-expect(forgeResult).toMatchObject({ status: "ok", threadBindingReady: true });
878+expectErrorResult(opsResult, "ops provider auth failed");
879+expectResultFields(forgeResult, { status: "ok", threadBindingReady: true });
870880});
871881});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。