mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-28 18:33:37 +00:00
fix(bluebubbles): refactor sendMessageBlueBubbles to use resolveBlueBubblesServerAccount and enhance private network handling in tests
This commit is contained in:
@@ -9,7 +9,7 @@ import {
|
||||
installBlueBubblesFetchTestHooks,
|
||||
mockBlueBubblesPrivateApiStatusOnce,
|
||||
} from "./test-harness.js";
|
||||
import type { BlueBubblesSendTarget } from "./types.js";
|
||||
import { _setFetchGuardForTesting, type BlueBubblesSendTarget } from "./types.js";
|
||||
|
||||
const mockFetch = vi.fn();
|
||||
const privateApiStatusMock = vi.mocked(getCachedBlueBubblesPrivateApiStatus);
|
||||
@@ -61,6 +61,33 @@ function mockNewChatSendResponse(guid: string) {
|
||||
});
|
||||
}
|
||||
|
||||
function installSsrFPolicyCapture(policies: unknown[]) {
|
||||
_setFetchGuardForTesting(async (params) => {
|
||||
policies.push(params.policy);
|
||||
const raw = await globalThis.fetch(params.url, params.init);
|
||||
let body: ArrayBuffer;
|
||||
if (typeof raw.arrayBuffer === "function") {
|
||||
body = await raw.arrayBuffer();
|
||||
} else {
|
||||
const text =
|
||||
typeof (raw as { text?: () => Promise<string> }).text === "function"
|
||||
? await (raw as { text: () => Promise<string> }).text()
|
||||
: typeof (raw as { json?: () => Promise<unknown> }).json === "function"
|
||||
? JSON.stringify(await (raw as { json: () => Promise<unknown> }).json())
|
||||
: "";
|
||||
body = new TextEncoder().encode(text).buffer;
|
||||
}
|
||||
return {
|
||||
response: new Response(body, {
|
||||
status: (raw as { status?: number }).status ?? 200,
|
||||
headers: (raw as { headers?: HeadersInit }).headers,
|
||||
}),
|
||||
release: async () => {},
|
||||
finalUrl: params.url,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
describe("send", () => {
|
||||
describe("resolveChatGuidForTarget", () => {
|
||||
const resolveHandleTargetGuid = async (data: Array<Record<string, unknown>>) => {
|
||||
@@ -448,6 +475,44 @@ describe("send", () => {
|
||||
expect(body.method).toBeUndefined();
|
||||
});
|
||||
|
||||
it("auto-enables private-network fetches for loopback serverUrl when allowPrivateNetwork is not set", async () => {
|
||||
const policies: unknown[] = [];
|
||||
installSsrFPolicyCapture(policies);
|
||||
mockResolvedHandleTarget();
|
||||
mockSendResponse({ data: { guid: "msg-loopback" } });
|
||||
|
||||
try {
|
||||
const result = await sendMessageBlueBubbles("+15551234567", "Hello world!", {
|
||||
serverUrl: "http://localhost:1234",
|
||||
password: "test",
|
||||
});
|
||||
|
||||
expect(result.messageId).toBe("msg-loopback");
|
||||
expect(policies).toEqual([{ allowPrivateNetwork: true }, { allowPrivateNetwork: true }]);
|
||||
} finally {
|
||||
_setFetchGuardForTesting(null);
|
||||
}
|
||||
});
|
||||
|
||||
it("auto-enables private-network fetches for private IP serverUrl when allowPrivateNetwork is not set", async () => {
|
||||
const policies: unknown[] = [];
|
||||
installSsrFPolicyCapture(policies);
|
||||
mockResolvedHandleTarget();
|
||||
mockSendResponse({ data: { guid: "msg-private-ip" } });
|
||||
|
||||
try {
|
||||
const result = await sendMessageBlueBubbles("+15551234567", "Hello world!", {
|
||||
serverUrl: "http://192.168.1.5:1234",
|
||||
password: "test",
|
||||
});
|
||||
|
||||
expect(result.messageId).toBe("msg-private-ip");
|
||||
expect(policies).toEqual([{ allowPrivateNetwork: true }, { allowPrivateNetwork: true }]);
|
||||
} finally {
|
||||
_setFetchGuardForTesting(null);
|
||||
}
|
||||
});
|
||||
|
||||
it("strips markdown formatting from outbound messages", async () => {
|
||||
mockResolvedHandleTarget();
|
||||
mockSendResponse({ data: { guid: "msg-uuid-stripped" } });
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import crypto from "node:crypto";
|
||||
import { resolveBlueBubblesAccount } from "./accounts.js";
|
||||
import { resolveBlueBubblesServerAccount } from "./account-resolve.js";
|
||||
import {
|
||||
getCachedBlueBubblesPrivateApiStatus,
|
||||
isBlueBubblesPrivateApiStatusEnabled,
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
import type { OpenClawConfig } from "./runtime-api.js";
|
||||
import { stripMarkdown } from "./runtime-api.js";
|
||||
import { warnBlueBubbles } from "./runtime.js";
|
||||
import { normalizeSecretInputString } from "./secret-input.js";
|
||||
import { extractBlueBubblesMessageId, resolveBlueBubblesSendTarget } from "./send-helpers.js";
|
||||
import { extractHandleFromChatGuid, normalizeBlueBubblesHandle } from "./targets.js";
|
||||
import {
|
||||
@@ -446,24 +445,13 @@ export async function sendMessageBlueBubbles(
|
||||
throw new Error("BlueBubbles send requires text (message was empty after markdown removal)");
|
||||
}
|
||||
|
||||
const account = resolveBlueBubblesAccount({
|
||||
const { baseUrl, password, accountId, allowPrivateNetwork } = resolveBlueBubblesServerAccount({
|
||||
cfg: opts.cfg ?? {},
|
||||
accountId: opts.accountId,
|
||||
serverUrl: opts.serverUrl,
|
||||
password: opts.password,
|
||||
});
|
||||
const baseUrl =
|
||||
normalizeSecretInputString(opts.serverUrl) ||
|
||||
normalizeSecretInputString(account.config.serverUrl);
|
||||
const password =
|
||||
normalizeSecretInputString(opts.password) ||
|
||||
normalizeSecretInputString(account.config.password);
|
||||
if (!baseUrl) {
|
||||
throw new Error("BlueBubbles serverUrl is required");
|
||||
}
|
||||
if (!password) {
|
||||
throw new Error("BlueBubbles password is required");
|
||||
}
|
||||
const privateApiStatus = getCachedBlueBubblesPrivateApiStatus(account.accountId);
|
||||
const allowPrivateNetwork = account.config.allowPrivateNetwork === true;
|
||||
const privateApiStatus = getCachedBlueBubblesPrivateApiStatus(accountId);
|
||||
|
||||
const target = resolveBlueBubblesSendTarget(to);
|
||||
const chatGuid = await resolveChatGuidForTarget({
|
||||
|
||||
Reference in New Issue
Block a user