test(extensions): trim hot extension imports

This commit is contained in:
Peter Steinberger
2026-04-23 13:06:19 +01:00
parent 547b0c201d
commit 2e0e0654ce
7 changed files with 80 additions and 55 deletions

View File

@@ -11,13 +11,13 @@
// - #60715 BB health check fails on LAN/private serverUrl
// - #66869 move `?password=` → header auth (future-proofed via AuthStrategy)
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
import { isBlockedHostnameOrIp, type SsrFPolicy } from "openclaw/plugin-sdk/ssrf-runtime";
import { resolveBlueBubblesServerAccount } from "./account-resolve.js";
import { extractAttachments } from "./monitor-normalize.js";
import { postMultipartFormData } from "./multipart.js";
import { resolveRequestUrl } from "./request-url.js";
import { DEFAULT_ACCOUNT_ID } from "./runtime-api.js";
import type { OpenClawConfig } from "./runtime-api.js";
import { getBlueBubblesRuntime } from "./runtime.js";
import {

View File

@@ -0,0 +1,13 @@
import { createAccountListHelpers } from "openclaw/plugin-sdk/account-core";
const {
listConfiguredAccountIds,
listAccountIds,
resolveDefaultAccountId: resolveDefaultWhatsAppAccountId,
} = createAccountListHelpers("whatsapp");
export {
listConfiguredAccountIds,
listAccountIds as listWhatsAppAccountIds,
resolveDefaultWhatsAppAccountId,
};

View File

@@ -1,7 +1,6 @@
import fs from "node:fs";
import path from "node:path";
import {
createAccountListHelpers,
DEFAULT_ACCOUNT_ID,
normalizeAccountId,
resolveUserPath,
@@ -11,9 +10,16 @@ import type { DmPolicy, GroupPolicy, ReplyToMode } from "openclaw/plugin-sdk/con
import { resolveOAuthDir } from "openclaw/plugin-sdk/state-paths";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { resolveMergedWhatsAppAccountConfig } from "./account-config.js";
import {
listConfiguredAccountIds,
listWhatsAppAccountIds,
resolveDefaultWhatsAppAccountId,
} from "./account-ids.js";
import type { WhatsAppAccountConfig } from "./account-types.js";
import { hasWebCredsSync } from "./creds-files.js";
export { listWhatsAppAccountIds, resolveDefaultWhatsAppAccountId } from "./account-ids.js";
export type ResolvedWhatsAppAccount = {
accountId: string;
name?: string;
@@ -43,11 +49,6 @@ export type ResolvedWhatsAppAccount = {
export const DEFAULT_WHATSAPP_MEDIA_MAX_MB = 50;
const { listConfiguredAccountIds, listAccountIds, resolveDefaultAccountId } =
createAccountListHelpers("whatsapp");
export const listWhatsAppAccountIds = listAccountIds;
export const resolveDefaultWhatsAppAccountId = resolveDefaultAccountId;
export function listWhatsAppAuthDirs(cfg: OpenClawConfig): string[] {
const oauthDir = resolveOAuthDir();
const whatsappDir = path.join(oauthDir, "whatsapp");

View File

@@ -1,4 +1,5 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { getActiveWebListener, resolveWebAccountId } from "./active-listener.js";
vi.mock("openclaw/plugin-sdk/config-runtime", () => ({
loadConfig: () => ({
@@ -6,18 +7,19 @@ vi.mock("openclaw/plugin-sdk/config-runtime", () => ({
}),
}));
const registryMocks = vi.hoisted(() => ({
getRegisteredWhatsAppConnectionController: vi.fn(),
}));
vi.mock("./connection-controller-registry.js", () => ({
getRegisteredWhatsAppConnectionController:
registryMocks.getRegisteredWhatsAppConnectionController,
}));
const WHATSAPP_ACTIVE_LISTENER_TEST_CFG = {
channels: { whatsapp: { accounts: { work: { enabled: true } }, defaultAccount: "work" } },
};
type ActiveListenerModule = typeof import("./active-listener.js");
const activeListenerModuleUrl = new URL("./active-listener.ts", import.meta.url).href;
async function importActiveListenerModule(cacheBust: string): Promise<ActiveListenerModule> {
return (await import(`${activeListenerModuleUrl}?t=${cacheBust}`)) as ActiveListenerModule;
}
function makeListener() {
return {
sendMessage: vi.fn(async () => ({ messageId: "msg-1" })),
@@ -27,53 +29,43 @@ function makeListener() {
};
}
afterEach(() => {
vi.doUnmock("./connection-controller-registry.js");
beforeEach(() => {
registryMocks.getRegisteredWhatsAppConnectionController.mockReset();
});
describe("active WhatsApp listener view", () => {
it("reads controller-backed state across duplicate module instances", async () => {
it("reads controller-backed state", () => {
const listener = makeListener();
vi.doMock("./connection-controller-registry.js", () => ({
getRegisteredWhatsAppConnectionController: (accountId: string) =>
registryMocks.getRegisteredWhatsAppConnectionController.mockImplementation(
(accountId: string) =>
accountId === "work"
? {
getActiveListener: () => listener,
}
: null,
}));
);
const first = await importActiveListenerModule(`first-${Date.now()}`);
const second = await importActiveListenerModule(`second-${Date.now()}`);
expect(first.getActiveWebListener("work")).toBe(listener);
expect(second.getActiveWebListener("work")).toBe(listener);
expect(getActiveWebListener("work")).toBe(listener);
});
it("resolves the configured default account when accountId is omitted", async () => {
it("resolves the configured default account when accountId is omitted", () => {
const listener = makeListener();
vi.doMock("./connection-controller-registry.js", () => ({
getRegisteredWhatsAppConnectionController: (accountId: string) =>
registryMocks.getRegisteredWhatsAppConnectionController.mockImplementation(
(accountId: string) =>
accountId === "work"
? {
getActiveListener: () => listener,
}
: null,
}));
);
const mod = await importActiveListenerModule(`default-${Date.now()}`);
expect(mod.resolveWebAccountId({ cfg: WHATSAPP_ACTIVE_LISTENER_TEST_CFG })).toBe("work");
expect(mod.getActiveWebListener("work")).toBe(listener);
expect(resolveWebAccountId({ cfg: WHATSAPP_ACTIVE_LISTENER_TEST_CFG })).toBe("work");
expect(getActiveWebListener("work")).toBe(listener);
});
it("returns null when the controller has no active listener for the account", async () => {
vi.doMock("./connection-controller-registry.js", () => ({
getRegisteredWhatsAppConnectionController: () => null,
}));
it("returns null when the controller has no active listener for the account", () => {
registryMocks.getRegisteredWhatsAppConnectionController.mockReturnValue(null);
const mod = await importActiveListenerModule(`missing-${Date.now()}`);
expect(mod.getActiveWebListener("work")).toBeNull();
expect(getActiveWebListener("work")).toBeNull();
});
});

View File

@@ -1,5 +1,5 @@
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
import { resolveDefaultWhatsAppAccountId } from "./accounts.js";
import { resolveDefaultWhatsAppAccountId } from "./account-ids.js";
import { getRegisteredWhatsAppConnectionController } from "./connection-controller-registry.js";
import type { ActiveWebListener, ActiveWebSendOptions } from "./inbound/types.js";

View File

@@ -2,9 +2,8 @@ import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/routing";
import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime-env";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { createQueuedWizardPrompter } from "../../../test/helpers/plugins/setup-wizard.js";
import { whatsappApprovalAuth } from "./approval-auth.js";
import { WHATSAPP_AUTH_UNSTABLE_CODE } from "./auth-store.js";
import { whatsappPlugin } from "./channel.js";
import { whatsappSetupPlugin } from "./channel.setup.js";
import { checkWhatsAppHeartbeatReady } from "./heartbeat.js";
import type { OpenClawConfig } from "./runtime-api.js";
import { finalizeWhatsAppSetup } from "./setup-finalize.js";
@@ -160,13 +159,6 @@ describe("whatsapp setup wizard", () => {
hoisted.resolveWhatsAppAuthDir.mockReturnValue({ authDir: "/tmp/openclaw-whatsapp-test" });
});
it("exposes approval auth through approvalCapability only", () => {
expect(whatsappPlugin.approvalCapability).toBe(whatsappApprovalAuth);
expect(typeof whatsappPlugin.auth?.login).toBe("function");
expect("authorizeActorAction" in (whatsappPlugin.auth ?? {})).toBe(false);
expect("getActionAvailabilityState" in (whatsappPlugin.auth ?? {})).toBe(false);
});
it("applies owner allowlist when forceAllowFrom is enabled", async () => {
const harness = createWhatsAppOwnerAllowlistHarness(createQueuedWizardPrompter);
@@ -245,7 +237,7 @@ describe("whatsapp setup wizard", () => {
});
it("surfaces accounts.default group warning paths for named accounts", () => {
const warnings = whatsappPlugin.security?.collectWarnings?.({
const warnings = whatsappSetupPlugin.security?.collectWarnings?.({
cfg: {
channels: {
whatsapp: {
@@ -277,7 +269,7 @@ describe("whatsapp setup wizard", () => {
});
it("surfaces mixed-case default-account group warning paths for named accounts", () => {
const warnings = whatsappPlugin.security?.collectWarnings?.({
const warnings = whatsappSetupPlugin.security?.collectWarnings?.({
cfg: {
channels: {
whatsapp: {
@@ -458,7 +450,7 @@ describe("whatsapp setup wizard", () => {
hoisted.readWebAuthState.mockResolvedValueOnce("unstable");
await expect(
whatsappPlugin.config.isConfigured?.(
whatsappSetupPlugin.config.isConfigured?.(
{
authDir: "/tmp/work",
} as never,

View File

@@ -0,0 +1,27 @@
import { describe, expect, it, vi } from "vitest";
type RegistryModule = typeof import("./connection-controller-registry.js");
const registryModuleUrl = new URL("./connection-controller-registry.ts", import.meta.url).href;
async function importRegistryModule(cacheBust: string): Promise<RegistryModule> {
return (await import(`${registryModuleUrl}?t=${cacheBust}`)) as RegistryModule;
}
describe("WhatsApp connection controller registry", () => {
it("shares registered controllers across duplicate module instances", async () => {
const first = await importRegistryModule(`first-${Date.now()}`);
const second = await importRegistryModule(`second-${Date.now()}`);
const controller = {
getActiveListener: vi.fn(() => null),
};
first.registerWhatsAppConnectionController("work", controller);
try {
expect(second.getRegisteredWhatsAppConnectionController("work")).toBe(controller);
} finally {
first.unregisterWhatsAppConnectionController("work", controller);
}
});
});