
























@@ -0,0 +1,81 @@
1+import { describe, expect, it } from "vitest";
2+import type { OpenClawConfig } from "./runtime-api.js";
3+import { resolveBlueBubblesOutboundSessionRoute } from "./session-route.js";
4+5+const EMPTY_CFG = {} as OpenClawConfig;
6+7+function call(target: string) {
8+return resolveBlueBubblesOutboundSessionRoute({
9+cfg: EMPTY_CFG,
10+agentId: "agent-1",
11+accountId: "default",
12+ target,
13+});
14+}
15+16+describe("resolveBlueBubblesOutboundSessionRoute DM/group disambiguation", () => {
17+it("treats `chat_guid:` with `;-;` marker as a DM", () => {
18+// Candidate-2 regression: the previous implementation classified ANY
19+// chat_guid-prefixed target as a group, even DMs (BlueBubbles encodes
20+// DM chatGuids as `service;-;handle`). That made the same DM resolve
21+// to one sessionKey via handle form (`+15551234567`) and a different
22+// sessionKey via chat_guid form (`chat_guid:iMessage;-;+15551234567`),
23+// causing bound DM sessions to mis-route into a freshly synthesized
24+// "group" session key.
25+const route = call("bluebubbles:chat_guid:iMessage;-;+15551234567");
26+expect(route).not.toBeNull();
27+expect(route?.peer.kind).toBe("direct");
28+expect(route?.chatType).toBe("direct");
29+expect(route?.from).toMatch(/^bluebubbles:/);
30+expect(route?.from).not.toMatch(/^group:/);
31+});
32+33+it("treats `chat_guid:` with `;+;` marker as a group", () => {
34+const route = call("bluebubbles:chat_guid:iMessage;+;chat-known-123");
35+expect(route).not.toBeNull();
36+expect(route?.peer.kind).toBe("group");
37+expect(route?.chatType).toBe("group");
38+expect(route?.from).toMatch(/^group:/);
39+});
40+41+it("falls back to group when chat_guid lacks a recognizable marker", () => {
42+// Backwards-compatible default: pre-fix behavior was to treat all
43+// chat_guid forms as group. Preserve that for unknown shapes so we
44+// do not silently downgrade an actual group to direct.
45+const route = call("bluebubbles:chat_guid:weird-no-semicolons");
46+expect(route).not.toBeNull();
47+expect(route?.peer.kind).toBe("group");
48+});
49+50+it("treats handle targets as direct", () => {
51+const route = call("bluebubbles:imessage:+15551234567");
52+expect(route).not.toBeNull();
53+expect(route?.peer.kind).toBe("direct");
54+expect(route?.from).toMatch(/^bluebubbles:/);
55+});
56+57+it("keeps chat_id targets classified as group", () => {
58+const route = call("bluebubbles:chat_id:42");
59+expect(route).not.toBeNull();
60+expect(route?.peer.kind).toBe("group");
61+expect(route?.peer.id).toBe("42");
62+});
63+64+it("keeps chat_identifier targets classified as group", () => {
65+const route = call("bluebubbles:chat_identifier:chat-abc");
66+expect(route).not.toBeNull();
67+expect(route?.peer.kind).toBe("group");
68+expect(route?.peer.id).toBe("chat-abc");
69+});
70+71+it("DM via chat_guid and DM via handle land on the same session key", () => {
72+// The point of disambiguation: a DM addressed two different ways must
73+// converge on the same sessionKey so existing bindings keep matching.
74+const handleRoute = call("bluebubbles:imessage:+15551234567");
75+const chatGuidRoute = call("bluebubbles:chat_guid:iMessage;-;+15551234567");
76+expect(handleRoute?.sessionKey).toBeDefined();
77+expect(chatGuidRoute?.sessionKey).toBeDefined();
78+// Both are direct now; sessionKey base derives from peer.id.
79+expect(handleRoute?.peer.kind).toBe(chatGuidRoute?.peer.kind);
80+});
81+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。