test(feishu): type reply lifecycle fixtures

This commit is contained in:
Ayaan Zaidi
2026-03-27 11:54:03 +05:30
parent 6ad50ce474
commit 59a25978dd
3 changed files with 63 additions and 51 deletions

View File

@@ -1,4 +1,5 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { createPluginRuntimeMock } from "../../../test/helpers/extensions/plugin-runtime-mock.js";
import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js";
import type { ClawdbotConfig, PluginRuntime } from "../runtime-api.js";
import type { FeishuMessageEvent } from "./bot.js";
@@ -73,7 +74,7 @@ describe("broadcast dispatch", () => {
},
},
},
} as unknown as ClawdbotConfig;
};
}
function createBroadcastEvent(options: {
@@ -122,38 +123,40 @@ describe("broadcast dispatch", () => {
},
},
});
setFeishuRuntime({
system: {
enqueueSystemEvent: vi.fn(),
},
channel: {
routing: {
resolveAgentRoute: mockResolveAgentRoute,
setFeishuRuntime(
createPluginRuntimeMock({
system: {
enqueueSystemEvent: vi.fn(),
},
reply: {
resolveEnvelopeFormatOptions: vi.fn(() => ({ template: "channel+name+time" })),
formatAgentEnvelope: vi.fn((params: { body: string }) => params.body),
finalizeInboundContext: mockFinalizeInboundContext,
dispatchReplyFromConfig: mockDispatchReplyFromConfig,
withReplyDispatcher: mockWithReplyDispatcher,
},
commands: {
shouldComputeCommandAuthorized: mockShouldComputeCommandAuthorized,
resolveCommandAuthorizedFromAuthorizers: vi.fn(() => false),
channel: {
routing: {
resolveAgentRoute: (params) => mockResolveAgentRoute(params),
},
reply: {
resolveEnvelopeFormatOptions: vi.fn(() => ({ template: "channel+name+time" })),
formatAgentEnvelope: vi.fn((params: { body: string }) => params.body),
finalizeInboundContext: mockFinalizeInboundContext,
dispatchReplyFromConfig: mockDispatchReplyFromConfig,
withReplyDispatcher: mockWithReplyDispatcher,
},
commands: {
shouldComputeCommandAuthorized: mockShouldComputeCommandAuthorized,
resolveCommandAuthorizedFromAuthorizers: vi.fn(() => false),
},
media: {
saveMediaBuffer: mockSaveMediaBuffer,
},
pairing: {
readAllowFromStore: vi.fn().mockResolvedValue([]),
upsertPairingRequest: vi.fn().mockResolvedValue({ code: "ABCDEFGH", created: false }),
buildPairingReply: vi.fn(() => "Pairing response"),
},
},
media: {
saveMediaBuffer: mockSaveMediaBuffer,
detectMime: vi.fn(async () => "application/octet-stream"),
},
pairing: {
readAllowFromStore: vi.fn().mockResolvedValue([]),
upsertPairingRequest: vi.fn().mockResolvedValue({ code: "ABCDEFGH", created: false }),
buildPairingReply: vi.fn(() => "Pairing response"),
},
},
media: {
detectMime: vi.fn(async () => "application/octet-stream"),
},
} as unknown as PluginRuntime);
}),
);
});
it("dispatches to all broadcast agents when bot is mentioned", async () => {
@@ -229,7 +232,7 @@ describe("broadcast dispatch", () => {
},
},
},
} as ClawdbotConfig;
};
const event: FeishuMessageEvent = {
sender: { sender_id: { open_id: "ou-sender" } },
@@ -270,7 +273,7 @@ describe("broadcast dispatch", () => {
},
},
},
} as unknown as ClawdbotConfig;
};
const event: FeishuMessageEvent = {
sender: { sender_id: { open_id: "ou-sender" } },
@@ -316,7 +319,7 @@ describe("broadcast dispatch", () => {
},
},
},
} as unknown as ClawdbotConfig;
};
const event: FeishuMessageEvent = {
sender: { sender_id: { open_id: "ou-sender" } },

View File

@@ -13,7 +13,7 @@ import {
import { createNonExitingRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js";
import type { ClawdbotConfig, RuntimeEnv } from "../runtime-api.js";
import { getFeishuLifecycleTestMocks } from "./lifecycle.test-support.js";
import type { ResolvedFeishuAccount } from "./types.js";
import type { FeishuConfig, ResolvedFeishuAccount } from "./types.js";
const {
createEventDispatcherMock,
@@ -88,6 +88,18 @@ function createLifecycleConfig(): ClawdbotConfig {
}
function createLifecycleAccount(accountId: "account-A" | "account-B"): ResolvedFeishuAccount {
const config: FeishuConfig = {
enabled: true,
connectionMode: "websocket",
groupPolicy: "open",
requireMention: false,
resolveSenderNames: false,
groups: {
oc_broadcast_group: {
requireMention: false,
},
},
};
return {
accountId,
selectionSource: "explicit",
@@ -96,19 +108,8 @@ function createLifecycleAccount(accountId: "account-A" | "account-B"): ResolvedF
appId: accountId === "account-A" ? "cli_a" : "cli_b",
appSecret: accountId === "account-A" ? "secret_a" : "secret_b", // pragma: allowlist secret
domain: "feishu",
config: {
enabled: true,
connectionMode: "websocket",
groupPolicy: "open",
requireMention: false,
resolveSenderNames: false,
groups: {
oc_broadcast_group: {
requireMention: false,
},
},
},
} as unknown as ResolvedFeishuAccount;
config,
};
}
async function setupLifecycleMonitor(accountId: "account-A" | "account-B") {

View File

@@ -1,5 +1,13 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
type StreamingSessionStub = {
active: boolean;
start: ReturnType<typeof vi.fn>;
update: ReturnType<typeof vi.fn>;
close: ReturnType<typeof vi.fn>;
isActive: ReturnType<typeof vi.fn>;
};
const resolveFeishuAccountMock = vi.hoisted(() => vi.fn());
const getFeishuRuntimeMock = vi.hoisted(() => vi.fn());
const sendMessageFeishuMock = vi.hoisted(() => vi.fn());
@@ -11,7 +19,7 @@ const resolveReceiveIdTypeMock = vi.hoisted(() => vi.fn());
const createReplyDispatcherWithTypingMock = vi.hoisted(() => vi.fn());
const addTypingIndicatorMock = vi.hoisted(() => vi.fn(async () => ({ messageId: "om_msg" })));
const removeTypingIndicatorMock = vi.hoisted(() => vi.fn(async () => {}));
const streamingInstances = vi.hoisted(() => [] as any[]);
const streamingInstances = vi.hoisted((): StreamingSessionStub[] => []);
vi.mock("./accounts.js", () => ({
resolveFeishuAccount: resolveFeishuAccountMock,
@@ -667,16 +675,16 @@ describe("createFeishuReplyDispatcher streaming behavior", () => {
let shouldFailStart = true;
// Intercept streaming instance creation to make first start() reject
const origPush = streamingInstances.push;
streamingInstances.push = function (this: any[], ...args: any[]) {
const origPush = streamingInstances.push.bind(streamingInstances);
streamingInstances.push = (...args: StreamingSessionStub[]) => {
if (shouldFailStart) {
args[0].start = vi
.fn()
.mockRejectedValue(new Error("Create card request failed with HTTP 400"));
shouldFailStart = false;
}
return origPush.apply(this, args);
} as any;
return origPush(...args);
};
try {
createFeishuReplyDispatcher({