
























@@ -3,6 +3,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
3344const probeMock = vi.hoisted(() => ({
55getCachedIMessagePrivateApiStatus: vi.fn(),
6+probeIMessagePrivateApi: vi.fn(),
67}));
7889const runtimeMock = vi.hoisted(() => ({
@@ -13,8 +14,28 @@ const runtimeMock = vi.hoisted(() => ({
1314sendAttachment: vi.fn(),
1415}));
151617+const loggerMock = vi.hoisted(() => ({
18+warn: vi.fn(),
19+info: vi.fn(),
20+error: vi.fn(),
21+debug: vi.fn(),
22+trace: vi.fn(),
23+fatal: vi.fn(),
24+}));
25+26+vi.mock("openclaw/plugin-sdk/runtime-env", async () => {
27+const actual = await vi.importActual<typeof import("openclaw/plugin-sdk/runtime-env")>(
28+"openclaw/plugin-sdk/runtime-env",
29+);
30+return {
31+ ...actual,
32+createSubsystemLogger: () => loggerMock,
33+};
34+});
35+1636vi.mock("./probe.js", () => ({
1737getCachedIMessagePrivateApiStatus: probeMock.getCachedIMessagePrivateApiStatus,
38+probeIMessagePrivateApi: probeMock.probeIMessagePrivateApi,
1839}));
19402041vi.mock("./private-api-status.js", () => ({
@@ -48,6 +69,8 @@ describe("imessage message actions", () => {
4869runtimeMock.sendRichMessage.mockReset();
4970runtimeMock.sendAttachment.mockReset();
5071probeMock.getCachedIMessagePrivateApiStatus.mockReset();
72+probeMock.probeIMessagePrivateApi.mockReset();
73+loggerMock.warn.mockReset();
5174});
52755376it("does not advertise private API actions when the bridge is known unavailable", () => {
@@ -130,6 +153,33 @@ describe("imessage message actions", () => {
130153expect(described?.actions).toContain("edit");
131154});
132155156+it("emits a channels/imessage WARN when the private API bridge is unavailable", async () => {
157+probeMock.getCachedIMessagePrivateApiStatus.mockReturnValue(undefined);
158+probeMock.probeIMessagePrivateApi.mockResolvedValue({
159+available: false,
160+v2Ready: false,
161+selectors: {},
162+});
163+164+await expect(
165+imessageMessageActions.handleAction?.({
166+action: "react",
167+cfg: cfg(),
168+params: {
169+chatGuid: "iMessage;+;chat0000",
170+messageId: "message-guid",
171+emoji: "👍",
172+},
173+} as never),
174+).rejects.toThrow(/imsg private API bridge/);
175+176+expect(loggerMock.warn).toHaveBeenCalledTimes(1);
177+const warnArg = String(loggerMock.warn.mock.calls[0][0]);
178+expect(warnArg).toMatch(/iMessage react blocked: private API bridge unavailable/);
179+expect(warnArg).toMatch(/imsg launch/);
180+expect(runtimeMock.sendReaction).not.toHaveBeenCalled();
181+});
182+133183it("rejects configured-off actions at execution time", async () => {
134184probeMock.getCachedIMessagePrivateApiStatus.mockReturnValue({
135185available: true,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。