import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { beforeEach, describe, expect, it, vi } from "vitest"; import * as replyModule from "../auto-reply/reply.js"; import type { ClawdbotConfig } from "../config/config.js"; import { resolveMainSessionKey } from "../config/sessions.js"; import { setActivePluginRegistry } from "../plugins/runtime.js"; import { createPluginRuntime } from "../plugins/runtime/index.js"; import { createTestRegistry } from "../test-utils/channel-plugins.js"; import { slackPlugin } from "../../extensions/slack/src/channel.js"; import { setSlackRuntime } from "../../extensions/slack/src/runtime.js"; import { telegramPlugin } from "../../extensions/telegram/src/channel.js"; import { setTelegramRuntime } from "../../extensions/telegram/src/runtime.js"; import { whatsappPlugin } from "../../extensions/whatsapp/src/channel.js"; import { setWhatsAppRuntime } from "../../extensions/whatsapp/src/runtime.js"; import { runHeartbeatOnce } from "./heartbeat-runner.js"; // Avoid pulling optional runtime deps during isolated runs. vi.mock("jiti", () => ({ createJiti: () => () => ({}) })); beforeEach(() => { const runtime = createPluginRuntime(); setSlackRuntime(runtime); setTelegramRuntime(runtime); setWhatsAppRuntime(runtime); setActivePluginRegistry( createTestRegistry([ { pluginId: "slack", plugin: slackPlugin, source: "test" }, { pluginId: "whatsapp", plugin: whatsappPlugin, source: "test" }, { pluginId: "telegram", plugin: telegramPlugin, source: "test" }, ]), ); }); describe("runHeartbeatOnce", () => { it("uses the delivery target as sender when lastTo differs", async () => { const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-hb-")); const storePath = path.join(tmpDir, "sessions.json"); const replySpy = vi.spyOn(replyModule, "getReplyFromConfig"); try { const cfg: ClawdbotConfig = { agents: { defaults: { workspace: tmpDir, heartbeat: { every: "5m", target: "slack", to: "C0A9P2N8QHY", }, }, }, session: { store: storePath }, }; const sessionKey = resolveMainSessionKey(cfg); await fs.writeFile( storePath, JSON.stringify( { [sessionKey]: { sessionId: "sid", updatedAt: Date.now(), lastChannel: "telegram", lastProvider: "telegram", lastTo: "1644620762", }, }, null, 2, ), ); replySpy.mockImplementation(async (ctx) => { expect(ctx.To).toBe("C0A9P2N8QHY"); expect(ctx.From).toBe("C0A9P2N8QHY"); return { text: "ok" }; }); const sendSlack = vi.fn().mockResolvedValue({ messageId: "m1", channelId: "C0A9P2N8QHY", }); await runHeartbeatOnce({ cfg, deps: { sendSlack, getQueueSize: () => 0, nowMs: () => 0, }, }); expect(sendSlack).toHaveBeenCalled(); } finally { replySpy.mockRestore(); await fs.rm(tmpDir, { recursive: true, force: true }); } }); });