mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
test(imessage): exercise chat lookup through runtime boundary (#121205)
This commit is contained in:
committed by
GitHub
parent
5262874d64
commit
2904bbfc47
@@ -72,17 +72,6 @@ function chatListCacheSet(
|
||||
}
|
||||
}
|
||||
|
||||
export function normalizeDirectChatIdentifierForTest(raw: string): string {
|
||||
return normalizeDirectChatIdentifier(raw);
|
||||
}
|
||||
|
||||
export function findChatGuidForTest(
|
||||
chats: readonly Record<string, unknown>[],
|
||||
target: Extract<IMessageTarget, { kind: "chat_id" | "chat_identifier" }>,
|
||||
): string | null {
|
||||
return findChatGuid(chats, target);
|
||||
}
|
||||
|
||||
function findChatGuid(
|
||||
chats: readonly Record<string, unknown>[],
|
||||
target: Extract<IMessageTarget, { kind: "chat_id" | "chat_identifier" }>,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { access, readFile } from "node:fs/promises";
|
||||
import { basename, dirname } from "node:path";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { normalizeDirectChatIdentifier } from "./chat-context.js";
|
||||
|
||||
const createIMessageRpcClientMock = vi.hoisted(() => vi.fn());
|
||||
const runIMessageCliJsonCommandMock = vi.hoisted(() => vi.fn());
|
||||
@@ -19,8 +20,7 @@ vi.mock("./remote-file.js", () => ({
|
||||
withIMessageRemoteFile: withIMessageRemoteFileMock,
|
||||
}));
|
||||
|
||||
const { imessageActionsRuntime, findChatGuidForTest, normalizeDirectChatIdentifierForTest } =
|
||||
await import("./actions.runtime.js");
|
||||
const { imessageActionsRuntime } = await import("./actions.runtime.js");
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
@@ -672,111 +672,121 @@ describe("findChatGuid cross-format identifier resolution", () => {
|
||||
},
|
||||
];
|
||||
|
||||
it("matches a synthesized iMessage;-;<phone> target against the chats.list <phone> identifier", () => {
|
||||
const result = findChatGuidForTest(chatsList, {
|
||||
kind: "chat_identifier",
|
||||
chatIdentifier: "iMessage;-;+12069106512",
|
||||
});
|
||||
expect(result).toBe("any;-;+12069106512");
|
||||
});
|
||||
type ChatGuidCase = {
|
||||
name: string;
|
||||
cliPath: string;
|
||||
chats: Array<Record<string, unknown>>;
|
||||
target:
|
||||
| { kind: "chat_id"; chatId: number }
|
||||
| { kind: "chat_identifier"; chatIdentifier: string };
|
||||
expected: string | null;
|
||||
};
|
||||
|
||||
it("matches a synthesized SMS;-;<phone> target the same way", () => {
|
||||
const result = findChatGuidForTest(chatsList, {
|
||||
kind: "chat_identifier",
|
||||
chatIdentifier: "SMS;-;+12069106512",
|
||||
});
|
||||
expect(result).toBe("any;-;+12069106512");
|
||||
});
|
||||
it.each([
|
||||
{
|
||||
name: "matches a synthesized iMessage;-;<phone> target against the chats.list <phone> identifier",
|
||||
cliPath: "imsg-cross-format-imessage",
|
||||
chats: chatsList,
|
||||
target: { kind: "chat_identifier", chatIdentifier: "iMessage;-;+12069106512" },
|
||||
expected: "any;-;+12069106512",
|
||||
},
|
||||
{
|
||||
name: "matches a synthesized SMS;-;<phone> target the same way",
|
||||
cliPath: "imsg-cross-format-sms",
|
||||
chats: chatsList,
|
||||
target: { kind: "chat_identifier", chatIdentifier: "SMS;-;+12069106512" },
|
||||
expected: "any;-;+12069106512",
|
||||
},
|
||||
{
|
||||
name: "matches a bare <phone> identifier exactly",
|
||||
cliPath: "imsg-cross-format-bare",
|
||||
chats: chatsList,
|
||||
target: { kind: "chat_identifier", chatIdentifier: "+12069106512" },
|
||||
expected: "any;-;+12069106512",
|
||||
},
|
||||
{
|
||||
name: "matches an any;-;<phone> guid form against the chats.list guid column",
|
||||
cliPath: "imsg-cross-format-any",
|
||||
chats: chatsList,
|
||||
target: { kind: "chat_identifier", chatIdentifier: "any;-;+12069106512" },
|
||||
expected: "any;-;+12069106512",
|
||||
},
|
||||
{
|
||||
name: "matches a group chat by exact guid",
|
||||
cliPath: "imsg-cross-format-group-guid",
|
||||
chats: chatsList,
|
||||
target: { kind: "chat_identifier", chatIdentifier: "iMessage;+;chat0000" },
|
||||
expected: "iMessage;+;chat0000",
|
||||
},
|
||||
{
|
||||
name: "matches a group chat by chat_id",
|
||||
cliPath: "imsg-cross-format-chat-id",
|
||||
chats: chatsList,
|
||||
target: { kind: "chat_id", chatId: 7 },
|
||||
expected: "iMessage;+;chat0000",
|
||||
},
|
||||
{
|
||||
name: "does not coerce non-decimal chat ids from chats.list",
|
||||
cliPath: "imsg-cross-format-nondecimal-id",
|
||||
chats: [{ id: "0x7", identifier: "wrong", guid: "iMessage;+;wrong" }],
|
||||
target: { kind: "chat_id", chatId: 7 },
|
||||
expected: null,
|
||||
},
|
||||
{
|
||||
name: "returns null for a phone number that does not exist in chats.list",
|
||||
cliPath: "imsg-cross-format-missing-phone",
|
||||
chats: chatsList,
|
||||
target: { kind: "chat_identifier", chatIdentifier: "iMessage;-;+19999999999" },
|
||||
expected: null,
|
||||
},
|
||||
{
|
||||
name: "does not cross-match different phone numbers via the prefix-stripping path",
|
||||
cliPath: "imsg-cross-format-different-phone",
|
||||
chats: chatsList,
|
||||
target: { kind: "chat_identifier", chatIdentifier: "iMessage;-;+18001234567" },
|
||||
expected: null,
|
||||
},
|
||||
{
|
||||
name: "does not match a DM target against a group's chat_identifier",
|
||||
cliPath: "imsg-cross-format-group-mismatch",
|
||||
chats: chatsList,
|
||||
target: { kind: "chat_identifier", chatIdentifier: "iMessage;+;chat-not-here" },
|
||||
expected: null,
|
||||
},
|
||||
] satisfies ChatGuidCase[])("$name", async ({ cliPath, chats, target, expected }) => {
|
||||
const client = mockRpcChatList(chats);
|
||||
|
||||
it("matches a bare <phone> identifier exactly", () => {
|
||||
const result = findChatGuidForTest(chatsList, {
|
||||
kind: "chat_identifier",
|
||||
chatIdentifier: "+12069106512",
|
||||
});
|
||||
expect(result).toBe("any;-;+12069106512");
|
||||
});
|
||||
|
||||
it("matches an any;-;<phone> guid form against the chats.list guid column", () => {
|
||||
const result = findChatGuidForTest(chatsList, {
|
||||
kind: "chat_identifier",
|
||||
chatIdentifier: "any;-;+12069106512",
|
||||
});
|
||||
expect(result).toBe("any;-;+12069106512");
|
||||
});
|
||||
|
||||
it("matches a group chat by exact guid", () => {
|
||||
const result = findChatGuidForTest(chatsList, {
|
||||
kind: "chat_identifier",
|
||||
chatIdentifier: "iMessage;+;chat0000",
|
||||
});
|
||||
expect(result).toBe("iMessage;+;chat0000");
|
||||
});
|
||||
|
||||
it("matches a group chat by chat_id", () => {
|
||||
const result = findChatGuidForTest(chatsList, { kind: "chat_id", chatId: 7 });
|
||||
expect(result).toBe("iMessage;+;chat0000");
|
||||
});
|
||||
|
||||
it("does not coerce non-decimal chat ids from chats.list", () => {
|
||||
const result = findChatGuidForTest(
|
||||
[
|
||||
{
|
||||
id: "0x7",
|
||||
identifier: "wrong",
|
||||
guid: "iMessage;+;wrong",
|
||||
},
|
||||
],
|
||||
{ kind: "chat_id", chatId: 7 },
|
||||
await expect(
|
||||
imessageActionsRuntime.resolveChatGuidForTarget({
|
||||
target,
|
||||
options: { cliPath },
|
||||
conversationReadOrigin: "delegated",
|
||||
}),
|
||||
).resolves.toBe(expected);
|
||||
expect(client.request).toHaveBeenCalledWith(
|
||||
"chats.list",
|
||||
{ limit: 1000 },
|
||||
{ timeoutMs: undefined },
|
||||
);
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it("returns null for a phone number that does not exist in chats.list", () => {
|
||||
const result = findChatGuidForTest(chatsList, {
|
||||
kind: "chat_identifier",
|
||||
chatIdentifier: "iMessage;-;+19999999999",
|
||||
});
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it("does not cross-match different phone numbers via the prefix-stripping path", () => {
|
||||
const result = findChatGuidForTest(chatsList, {
|
||||
kind: "chat_identifier",
|
||||
chatIdentifier: "iMessage;-;+18001234567",
|
||||
});
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it("does not match a DM target against a group's chat_identifier", () => {
|
||||
const result = findChatGuidForTest(chatsList, {
|
||||
kind: "chat_identifier",
|
||||
chatIdentifier: "iMessage;+;chat-not-here",
|
||||
});
|
||||
expect(result).toBeNull();
|
||||
expect(client.stop).toHaveBeenCalledOnce();
|
||||
});
|
||||
});
|
||||
|
||||
describe("normalizeDirectChatIdentifier", () => {
|
||||
it("strips the iMessage;-; prefix", () => {
|
||||
expect(normalizeDirectChatIdentifierForTest("iMessage;-;+12069106512")).toBe("+12069106512");
|
||||
});
|
||||
it("strips the SMS;-; prefix", () => {
|
||||
expect(normalizeDirectChatIdentifierForTest("SMS;-;+12069106512")).toBe("+12069106512");
|
||||
});
|
||||
it("strips the any;-; prefix", () => {
|
||||
expect(normalizeDirectChatIdentifierForTest("any;-;+12069106512")).toBe("+12069106512");
|
||||
});
|
||||
it("matches case-insensitively", () => {
|
||||
expect(normalizeDirectChatIdentifierForTest("IMESSAGE;-;+12069106512")).toBe("+12069106512");
|
||||
});
|
||||
it("leaves group identifiers (iMessage;+;chat...) unchanged", () => {
|
||||
expect(normalizeDirectChatIdentifierForTest("iMessage;+;chat0000")).toBe("iMessage;+;chat0000");
|
||||
expect(normalizeDirectChatIdentifierForTest("iMessage;+;Some@example.com")).toBe(
|
||||
it.each([
|
||||
["strips the iMessage;-; prefix", "iMessage;-;+12069106512", "+12069106512"],
|
||||
["strips the SMS;-; prefix", "SMS;-;+12069106512", "+12069106512"],
|
||||
["strips the any;-; prefix", "any;-;+12069106512", "+12069106512"],
|
||||
["matches case-insensitively", "IMESSAGE;-;+12069106512", "+12069106512"],
|
||||
["leaves group identifiers unchanged", "iMessage;+;chat0000", "iMessage;+;chat0000"],
|
||||
[
|
||||
"leaves group email identifiers unchanged",
|
||||
"iMessage;+;Some@example.com",
|
||||
);
|
||||
});
|
||||
it("leaves bare values unchanged", () => {
|
||||
expect(normalizeDirectChatIdentifierForTest("+12069106512")).toBe("+12069106512");
|
||||
expect(normalizeDirectChatIdentifierForTest("foo@bar.com")).toBe("foo@bar.com");
|
||||
"iMessage;+;Some@example.com",
|
||||
],
|
||||
["leaves bare phone values unchanged", "+12069106512", "+12069106512"],
|
||||
["leaves bare email values unchanged", "foo@bar.com", "foo@bar.com"],
|
||||
])("%s", (_name, input, expected) => {
|
||||
expect(normalizeDirectChatIdentifier(input)).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,11 +3,7 @@ import { basename, parse, win32 } from "node:path";
|
||||
import { sanitizeUntrustedFileName } from "openclaw/plugin-sdk/security-runtime";
|
||||
import { resolvePreferredOpenClawTmpDir, withTempWorkspace } from "openclaw/plugin-sdk/temp-path";
|
||||
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
||||
import {
|
||||
findChatGuidForTest,
|
||||
normalizeDirectChatIdentifierForTest,
|
||||
resolveIMessageActionChatGuid,
|
||||
} from "./actions-chat-guid.js";
|
||||
import { resolveIMessageActionChatGuid } from "./actions-chat-guid.js";
|
||||
import {
|
||||
type IMessageActionTransportOptions,
|
||||
requestIMessageActionRpc,
|
||||
@@ -21,7 +17,6 @@ import {
|
||||
} from "./monitor-reply-cache.js";
|
||||
import { sanitizeIMessageFinalOutboundText } from "./monitor/sanitize-outbound.js";
|
||||
import { withIMessageRemoteFile } from "./remote-file.js";
|
||||
export { findChatGuidForTest, normalizeDirectChatIdentifierForTest };
|
||||
|
||||
type IMessageBridgeActionOptions = IMessageActionTransportOptions & {
|
||||
chatGuid: string;
|
||||
|
||||
Reference in New Issue
Block a user