


























@@ -135,6 +135,18 @@ describe("matrix thread bindings", () => {
135135return parsed.bindings?.[0]?.lastActivityAt;
136136}
137137138+async function readPersistedBindings(bindingsPath: string) {
139+const raw = await fs.readFile(bindingsPath, "utf-8");
140+return JSON.parse(raw) as {
141+version?: number;
142+bindings?: Array<{
143+conversationId?: string;
144+parentConversationId?: string;
145+targetSessionKey?: string;
146+}>;
147+};
148+}
149+138150async function expectPersistedThreadBinding(
139151bindingsPath: string,
140152expected: {
@@ -143,17 +155,22 @@ describe("matrix thread bindings", () => {
143155parentConversationId?: string;
144156},
145157) {
146-const persistedRaw = await fs.readFile(bindingsPath, "utf-8");
147-expect(JSON.parse(persistedRaw)).toMatchObject({
148-version: 1,
149-bindings: [
150-expect.objectContaining({
151-conversationId: expected.conversationId,
152-parentConversationId: expected.parentConversationId ?? "!room:example",
153-targetSessionKey: expected.targetSessionKey,
154-}),
155-],
156-});
158+const persisted = await readPersistedBindings(bindingsPath);
159+expect(persisted.version).toBe(1);
160+expect(persisted.bindings).toHaveLength(1);
161+expect(persisted.bindings?.[0]?.conversationId).toBe(expected.conversationId);
162+expect(persisted.bindings?.[0]?.parentConversationId).toBe(
163+expected.parentConversationId ?? "!room:example",
164+);
165+expect(persisted.bindings?.[0]?.targetSessionKey).toBe(expected.targetSessionKey);
166+}
167+168+function latestSendMessageCall() {
169+const call = sendMessageMatrixMock.mock.calls.at(-1);
170+if (!call) {
171+throw new Error("expected sendMessageMatrix call");
172+}
173+return call;
157174}
158175159176beforeEach(() => {
@@ -222,17 +239,14 @@ describe("matrix thread bindings", () => {
222239accountId: "ops",
223240threadId: "$thread",
224241});
225-expect(
226-getSessionBindingService().resolveByConversation({
227-channel: "matrix",
228-accountId: "ops",
229-conversationId: "$thread",
230-parentConversationId: "!room:example",
231-}),
232-).toMatchObject({
233-bindingId: binding.bindingId,
234-targetSessionKey: "agent:ops:subagent:child",
242+const resolved = getSessionBindingService().resolveByConversation({
243+channel: "matrix",
244+accountId: "ops",
245+conversationId: "$thread",
246+parentConversationId: "!room:example",
235247});
248+expect(resolved?.bindingId).toBe(binding.bindingId);
249+expect(resolved?.targetSessionKey).toBe("agent:ops:subagent:child");
236250});
237251238252it("expires idle bindings via the sweeper", async () => {
@@ -328,11 +342,9 @@ describe("matrix thread bindings", () => {
328342329343await vi.waitFor(
330344async () => {
331-const persistedRaw = await fs.readFile(resolveBindingsFilePath(), "utf-8");
332-expect(JSON.parse(persistedRaw)).toMatchObject({
333-version: 1,
334-bindings: [],
335-});
345+const persisted = await readPersistedBindings(resolveBindingsFilePath());
346+expect(persisted.version).toBe(1);
347+expect(persisted.bindings).toEqual([]);
336348},
337349{ interval: 1, timeout: 100 },
338350);
@@ -380,8 +392,12 @@ describe("matrix thread bindings", () => {
380392message.includes("failed auto-unbinding expired bindings"),
381393),
382394).toBe(true);
383-expect(logVerboseMessage).toHaveBeenCalledWith(
384-expect.stringContaining("matrix: auto-unbinding $thread due to idle-expired"),
395+expect(
396+logVerboseMessage.mock.calls.some(
397+([message]) =>
398+typeof message === "string" &&
399+message.includes("matrix: auto-unbinding $thread due to idle-expired"),
400+),
385401);
386402},
387403{ interval: 1, timeout: 100 },
@@ -432,15 +448,13 @@ describe("matrix thread bindings", () => {
432448reason: "idle-expired",
433449});
434450435-expect(sendMessageMatrixMock).toHaveBeenCalledWith(
436-"room:!room:example",
437-expect.stringContaining("Session ended automatically"),
438-expect.objectContaining({
439-cfg: {},
440-accountId: "ops",
441-threadId: "$thread",
442-}),
443-);
451+const [to, message, options] = latestSendMessageCall();
452+const sendOptions = options as { cfg?: unknown; accountId?: string; threadId?: string };
453+expect(to).toBe("room:!room:example");
454+expect(message).toContain("Session ended automatically");
455+expect(sendOptions.cfg).toEqual({});
456+expect(sendOptions.accountId).toBe("ops");
457+expect(sendOptions.threadId).toBe("$thread");
444458});
445459446460it("does not reload persisted bindings after the Matrix access token changes while deviceId is unknown", async () => {
@@ -524,10 +538,8 @@ describe("matrix thread bindings", () => {
524538accountId: "ops",
525539conversationId: "$thread",
526540parentConversationId: "!room:example",
527-}),
528-).toMatchObject({
529-targetSessionKey: "agent:ops:subagent:child",
530-});
541+})?.targetSessionKey,
542+).toBe("agent:ops:subagent:child");
531543532544const rotatedBindingsPath = path.join(
533545resolveMatrixStoragePaths({
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。