refactor(bluebubbles): centralize private-api status handling

This commit is contained in:
Peter Steinberger
2026-02-22 12:08:08 +01:00
parent 6f7e5f92c3
commit 296b3f49ef
8 changed files with 186 additions and 35 deletions

View File

@@ -1,15 +1,22 @@
import type { PluginRuntime } from "openclaw/plugin-sdk";
import { beforeEach, describe, expect, it, vi } from "vitest";
import "./test-mocks.js";
import { getCachedBlueBubblesPrivateApiStatus } from "./probe.js";
import { clearBlueBubblesRuntime, setBlueBubblesRuntime } from "./runtime.js";
import { sendMessageBlueBubbles, resolveChatGuidForTarget } from "./send.js";
import { installBlueBubblesFetchTestHooks } from "./test-harness.js";
import {
BLUE_BUBBLES_PRIVATE_API_STATUS,
installBlueBubblesFetchTestHooks,
mockBlueBubblesPrivateApiStatusOnce,
} from "./test-harness.js";
import type { BlueBubblesSendTarget } from "./types.js";
const mockFetch = vi.fn();
const privateApiStatusMock = vi.mocked(getCachedBlueBubblesPrivateApiStatus);
installBlueBubblesFetchTestHooks({
mockFetch,
privateApiStatusMock: vi.mocked(getCachedBlueBubblesPrivateApiStatus),
privateApiStatusMock,
});
function mockResolvedHandleTarget(
@@ -527,7 +534,10 @@ describe("send", () => {
});
it("uses private-api when reply metadata is present", async () => {
vi.mocked(getCachedBlueBubblesPrivateApiStatus).mockReturnValueOnce(true);
mockBlueBubblesPrivateApiStatusOnce(
privateApiStatusMock,
BLUE_BUBBLES_PRIVATE_API_STATUS.enabled,
);
mockResolvedHandleTarget();
mockSendResponse({ data: { guid: "msg-uuid-124" } });
@@ -549,7 +559,10 @@ describe("send", () => {
});
it("downgrades threaded reply to plain send when private API is disabled", async () => {
vi.mocked(getCachedBlueBubblesPrivateApiStatus).mockReturnValueOnce(false);
mockBlueBubblesPrivateApiStatusOnce(
privateApiStatusMock,
BLUE_BUBBLES_PRIVATE_API_STATUS.disabled,
);
mockResolvedHandleTarget();
mockSendResponse({ data: { guid: "msg-uuid-plain" } });
@@ -569,7 +582,10 @@ describe("send", () => {
});
it("normalizes effect names and uses private-api for effects", async () => {
vi.mocked(getCachedBlueBubblesPrivateApiStatus).mockReturnValueOnce(true);
mockBlueBubblesPrivateApiStatusOnce(
privateApiStatusMock,
BLUE_BUBBLES_PRIVATE_API_STATUS.enabled,
);
mockResolvedHandleTarget();
mockSendResponse({ data: { guid: "msg-uuid-125" } });
@@ -589,6 +605,8 @@ describe("send", () => {
});
it("warns and downgrades private-api features when status is unknown", async () => {
const runtimeLog = vi.fn();
setBlueBubblesRuntime({ log: runtimeLog } as unknown as PluginRuntime);
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
mockResolvedHandleTarget();
mockSendResponse({ data: { guid: "msg-uuid-unknown" } });
@@ -602,8 +620,9 @@ describe("send", () => {
});
expect(result.messageId).toBe("msg-uuid-unknown");
expect(warnSpy).toHaveBeenCalledTimes(1);
expect(warnSpy.mock.calls[0]?.[0]).toContain("Private API status unknown");
expect(runtimeLog).toHaveBeenCalledTimes(1);
expect(runtimeLog.mock.calls[0]?.[0]).toContain("Private API status unknown");
expect(warnSpy).not.toHaveBeenCalled();
const sendCall = mockFetch.mock.calls[1];
const body = JSON.parse(sendCall[1].body);
@@ -612,6 +631,7 @@ describe("send", () => {
expect(body.partIndex).toBeUndefined();
expect(body.effectId).toBeUndefined();
} finally {
clearBlueBubblesRuntime();
warnSpy.mockRestore();
}
});