import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; const hoisted = vi.hoisted(() => ({ loadConfig: vi.fn(), resolveMarkdownTableMode: vi.fn(() => "preserve"), convertMarkdownTables: vi.fn((text: string) => text), record: vi.fn(), resolveNextcloudTalkAccount: vi.fn(() => ({ accountId: "default", baseUrl: "https://nextcloud.example.com", secret: "secret-value", // pragma: allowlist secret })), generateNextcloudTalkSignature: vi.fn(() => ({ random: "r", signature: "s", })), })); vi.mock("./runtime.js", () => ({ getNextcloudTalkRuntime: () => ({ config: { loadConfig: hoisted.loadConfig, }, channel: { text: { resolveMarkdownTableMode: hoisted.resolveMarkdownTableMode, convertMarkdownTables: hoisted.convertMarkdownTables, }, activity: { record: hoisted.record, }, }, }), })); vi.mock("./accounts.js", () => ({ resolveNextcloudTalkAccount: hoisted.resolveNextcloudTalkAccount, })); vi.mock("./signature.js", () => ({ generateNextcloudTalkSignature: hoisted.generateNextcloudTalkSignature, })); import { sendMessageNextcloudTalk, sendReactionNextcloudTalk } from "./send.js"; describe("nextcloud-talk send cfg threading", () => { const fetchMock = vi.fn(); beforeEach(() => { vi.clearAllMocks(); fetchMock.mockReset(); vi.stubGlobal("fetch", fetchMock); }); afterEach(() => { vi.unstubAllGlobals(); }); it("uses provided cfg for sendMessage and skips runtime loadConfig", async () => { const cfg = { source: "provided" } as const; fetchMock.mockResolvedValueOnce( new Response( JSON.stringify({ ocs: { data: { id: 12345, timestamp: 1_706_000_000 } }, }), { status: 200, headers: { "content-type": "application/json" } }, ), ); const result = await sendMessageNextcloudTalk("room:abc123", "hello", { cfg, accountId: "work", }); expect(hoisted.loadConfig).not.toHaveBeenCalled(); expect(hoisted.resolveNextcloudTalkAccount).toHaveBeenCalledWith({ cfg, accountId: "work", }); expect(fetchMock).toHaveBeenCalledTimes(1); expect(result).toEqual({ messageId: "12345", roomToken: "abc123", timestamp: 1_706_000_000, }); }); it("falls back to runtime cfg for sendReaction when cfg is omitted", async () => { const runtimeCfg = { source: "runtime" } as const; hoisted.loadConfig.mockReturnValueOnce(runtimeCfg); fetchMock.mockResolvedValueOnce(new Response("{}", { status: 200 })); const result = await sendReactionNextcloudTalk("room:ops", "m-1", "👍", { accountId: "default", }); expect(result).toEqual({ ok: true }); expect(hoisted.loadConfig).toHaveBeenCalledTimes(1); expect(hoisted.resolveNextcloudTalkAccount).toHaveBeenCalledWith({ cfg: runtimeCfg, accountId: "default", }); }); });