test: share messaging plugin fixtures

This commit is contained in:
Peter Steinberger
2026-04-20 20:52:16 +01:00
parent 553cc80027
commit aa0957c4dd
6 changed files with 59 additions and 102 deletions

View File

@@ -1,4 +1,5 @@
import { vi, type Mock } from "vitest";
import { finalizeTelegramInboundContextForTest } from "./bot-message-context.session-runtime-test-support.js";
type AsyncUnknownMock = Mock<(...args: unknown[]) => Promise<unknown>>;
type BuildTelegramMessageContextForTest =
@@ -13,27 +14,9 @@ const hoisted = vi.hoisted((): { recordInboundSessionMock: AsyncUnknownMock } =>
}));
export const recordInboundSessionMock: AsyncUnknownMock = hoisted.recordInboundSessionMock;
const finalizeInboundContextForTest = ((ctx) => {
const next = ctx as Record<string, unknown>;
const body = typeof next.Body === "string" ? next.Body : "";
next.Body = body;
next.BodyForAgent =
typeof next.BodyForAgent === "string"
? next.BodyForAgent
: typeof next.RawBody === "string"
? next.RawBody
: body;
next.BodyForCommands =
typeof next.BodyForCommands === "string"
? next.BodyForCommands
: typeof next.CommandBody === "string"
? next.CommandBody
: typeof next.RawBody === "string"
? next.RawBody
: body;
next.CommandAuthorized = Boolean(next.CommandAuthorized);
return next;
}) as NonNullable<TelegramTestSessionRuntime["finalizeInboundContext"]>;
const finalizeInboundContextForTest = finalizeTelegramInboundContextForTest as NonNullable<
TelegramTestSessionRuntime["finalizeInboundContext"]
>;
const recordInboundSessionForTest: NonNullable<
TelegramTestSessionRuntime["recordInboundSession"]
> = async (params) => {

View File

@@ -0,0 +1,21 @@
export function finalizeTelegramInboundContextForTest(ctx: unknown): Record<string, unknown> {
const next = ctx as Record<string, unknown>;
const body = typeof next.Body === "string" ? next.Body : "";
next.Body = body;
next.BodyForAgent =
typeof next.BodyForAgent === "string"
? next.BodyForAgent
: typeof next.RawBody === "string"
? next.RawBody
: body;
next.BodyForCommands =
typeof next.BodyForCommands === "string"
? next.BodyForCommands
: typeof next.CommandBody === "string"
? next.CommandBody
: typeof next.RawBody === "string"
? next.RawBody
: body;
next.CommandAuthorized = Boolean(next.CommandAuthorized);
return next;
}

View File

@@ -1,4 +1,5 @@
import type { BuildTelegramMessageContextParams, TelegramMediaRef } from "./bot-message-context.js";
import { finalizeTelegramInboundContextForTest } from "./bot-message-context.session-runtime-test-support.js";
export const baseTelegramMessageContextConfig = {
agents: { defaults: { model: "anthropic/claude-opus-4-5", workspace: "/tmp/openclaw" } },
@@ -7,27 +8,9 @@ export const baseTelegramMessageContextConfig = {
} as never;
type TelegramTestSessionRuntime = NonNullable<BuildTelegramMessageContextParams["sessionRuntime"]>;
const finalizeInboundContextForTest = ((ctx) => {
const next = ctx as Record<string, unknown>;
const body = typeof next.Body === "string" ? next.Body : "";
next.Body = body;
next.BodyForAgent =
typeof next.BodyForAgent === "string"
? next.BodyForAgent
: typeof next.RawBody === "string"
? next.RawBody
: body;
next.BodyForCommands =
typeof next.BodyForCommands === "string"
? next.BodyForCommands
: typeof next.CommandBody === "string"
? next.CommandBody
: typeof next.RawBody === "string"
? next.RawBody
: body;
next.CommandAuthorized = Boolean(next.CommandAuthorized);
return next;
}) as NonNullable<TelegramTestSessionRuntime["finalizeInboundContext"]>;
const finalizeInboundContextForTest = finalizeTelegramInboundContextForTest as NonNullable<
TelegramTestSessionRuntime["finalizeInboundContext"]
>;
type BuildTelegramMessageContextForTestParams = {
message: Record<string, unknown>;

View File

@@ -1,4 +1,3 @@
import type { ChannelDirectoryEntry } from "openclaw/plugin-sdk/channel-contract";
import type { OpenClawConfig } from "openclaw/plugin-sdk/testing";
import { describe, expect, it } from "vitest";
import {
@@ -6,29 +5,8 @@ import {
listWhatsAppDirectoryPeersFromConfig,
} from "../directory-contract-api.js";
type DirectoryListFn = (params: {
cfg: OpenClawConfig;
accountId?: string;
query?: string | null;
limit?: number | null;
}) => Promise<ChannelDirectoryEntry[]>;
async function listDirectoryEntriesWithDefaults(listFn: DirectoryListFn, cfg: OpenClawConfig) {
return await listFn({
cfg,
accountId: "default",
query: null,
limit: null,
});
}
async function expectDirectoryIds(
listFn: DirectoryListFn,
cfg: OpenClawConfig,
expected: string[],
) {
const entries = await listDirectoryEntriesWithDefaults(listFn, cfg);
expect(entries.map((entry) => entry.id)).toEqual(expected);
function ids(entries: Array<{ id: string }>) {
return entries.map((entry) => entry.id);
}
describe("WhatsApp directory contract", () => {
@@ -42,8 +20,20 @@ describe("WhatsApp directory contract", () => {
},
} as unknown as OpenClawConfig;
await expectDirectoryIds(listWhatsAppDirectoryPeersFromConfig, cfg, ["+15550000000"]);
await expectDirectoryIds(listWhatsAppDirectoryGroupsFromConfig, cfg, ["999@g.us"]);
const peers = await listWhatsAppDirectoryPeersFromConfig({
cfg,
accountId: "default",
query: null,
limit: null,
});
const groups = await listWhatsAppDirectoryGroupsFromConfig({
cfg,
accountId: "default",
query: null,
limit: null,
});
expect(ids(peers)).toEqual(["+15550000000"]);
expect(ids(groups)).toEqual(["999@g.us"]);
});
it("applies query and limit filtering for config-backed directories", async () => {
@@ -61,6 +51,6 @@ describe("WhatsApp directory contract", () => {
query: "@g.us",
limit: 1,
});
expect(groups.map((entry) => entry.id)).toEqual(["111@g.us"]);
expect(ids(groups)).toEqual(["111@g.us"]);
});
});

View File

@@ -2,31 +2,14 @@ import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
import { describe, expect, it } from "vitest";
import { resolveWhatsAppReactionLevel } from "./reaction-level.js";
type ReactionResolution = ReturnType<typeof resolveWhatsAppReactionLevel>;
describe("resolveWhatsAppReactionLevel", () => {
const expectReactionFlags = (
result: ReactionResolution,
expected: {
level: "off" | "ack" | "minimal" | "extensive";
ackEnabled: boolean;
agentReactionsEnabled: boolean;
agentReactionGuidance?: "minimal" | "extensive";
},
) => {
expect(result.level).toBe(expected.level);
expect(result.ackEnabled).toBe(expected.ackEnabled);
expect(result.agentReactionsEnabled).toBe(expected.agentReactionsEnabled);
expect(result.agentReactionGuidance).toBe(expected.agentReactionGuidance);
};
it("defaults to minimal level when reactionLevel is not set", () => {
const cfg: OpenClawConfig = {
channels: { whatsapp: {} },
};
const result = resolveWhatsAppReactionLevel({ cfg });
expectReactionFlags(result, {
expect(result).toEqual({
level: "minimal",
ackEnabled: false,
agentReactionsEnabled: true,
@@ -40,7 +23,7 @@ describe("resolveWhatsAppReactionLevel", () => {
};
const result = resolveWhatsAppReactionLevel({ cfg });
expectReactionFlags(result, {
expect(result).toEqual({
level: "off",
ackEnabled: false,
agentReactionsEnabled: false,
@@ -53,7 +36,7 @@ describe("resolveWhatsAppReactionLevel", () => {
};
const result = resolveWhatsAppReactionLevel({ cfg });
expectReactionFlags(result, {
expect(result).toEqual({
level: "ack",
ackEnabled: true,
agentReactionsEnabled: false,
@@ -66,7 +49,7 @@ describe("resolveWhatsAppReactionLevel", () => {
};
const result = resolveWhatsAppReactionLevel({ cfg });
expectReactionFlags(result, {
expect(result).toEqual({
level: "minimal",
ackEnabled: false,
agentReactionsEnabled: true,
@@ -80,7 +63,7 @@ describe("resolveWhatsAppReactionLevel", () => {
};
const result = resolveWhatsAppReactionLevel({ cfg });
expectReactionFlags(result, {
expect(result).toEqual({
level: "extensive",
ackEnabled: false,
agentReactionsEnabled: true,
@@ -101,7 +84,7 @@ describe("resolveWhatsAppReactionLevel", () => {
};
const result = resolveWhatsAppReactionLevel({ cfg, accountId: "work" });
expectReactionFlags(result, {
expect(result).toEqual({
level: "extensive",
ackEnabled: false,
agentReactionsEnabled: true,

View File

@@ -65,25 +65,22 @@ vi.mock("openclaw/plugin-sdk/setup", async () => {
vi.mock("./accounts.js", async () => {
const actual = await vi.importActual<typeof import("./accounts.js")>("./accounts.js");
return {
...actual,
return Object.assign({}, actual, {
resolveWhatsAppAuthDir: hoisted.resolveWhatsAppAuthDir,
};
});
});
vi.mock("./auth-store.js", async () => {
const actual = await vi.importActual<typeof import("./auth-store.js")>("./auth-store.js");
return {
...actual,
return Object.assign({}, actual, {
readWebAuthState: hoisted.readWebAuthState,
};
});
});
function createRuntime(): RuntimeEnv {
return {
const createRuntime = (): RuntimeEnv =>
({
error: vi.fn(),
} as unknown as RuntimeEnv;
}
}) as unknown as RuntimeEnv;
const whatsappGetStatus = createPluginSetupWizardStatus({
id: "whatsapp",