fix(hub): 稳定 Feishu trigger 集成测试

This commit is contained in:
2026-07-08 15:13:50 +08:00
parent ecbc2c8666
commit ead5cf04d6
5 changed files with 167 additions and 28 deletions
+44 -1
View File
@@ -57,23 +57,41 @@ export const silentLogger: FastifyBaseLogger = {
export interface MockFeishuRuntime extends FeishuRuntime {
readonly sentTexts: string[];
readonly sentCards: unknown[];
readonly sentPatches: unknown[];
readonly reactions: Array<{ readonly messageId: string; readonly emoji: string }>;
}
export function mockFeishuRuntime(): MockFeishuRuntime {
const sentTexts: string[] = [];
const sentCards: unknown[] = [];
const sentPatches: unknown[] = [];
const reactions: Array<{ messageId: string; emoji: string }> = [];
// Mock the client — sendText/sendCard are in client.ts, not on the runtime
// object directly. We patch by providing a runtime whose client is a stub;
// the actual send functions cast through shape, so a minimal stub works.
const rt: MockFeishuRuntime = {
client: {
request: async (p: unknown) => {
const payload = p as { url?: string; data?: { reaction_type?: { emoji_type?: string } } };
const match = payload.url?.match(/\/messages\/([^/]+)\/reactions$/);
if (match?.[1] !== undefined) {
reactions.push({
messageId: match[1],
emoji: payload.data?.reaction_type?.emoji_type ?? "",
});
}
return {};
},
im: {
v1: {
message: {
create: async (p: unknown) => {
const payload = p as { data?: { msg_type?: string; content?: string } };
if (payload.data?.msg_type === "interactive") {
sentCards.push(payload.data.content ? JSON.parse(payload.data.content) : null);
const card = payload.data.content ? JSON.parse(payload.data.content) : null;
sentCards.push(card);
const text = textFromCard(card);
if (text !== null) sentTexts.push(text);
} else {
try {
const c = JSON.parse(payload.data?.content ?? "{}") as { text?: string };
@@ -84,6 +102,14 @@ export function mockFeishuRuntime(): MockFeishuRuntime {
}
return { data: { message_id: "mock-msg-id" } };
},
patch: async (p: unknown) => {
const payload = p as { data?: { content?: string } };
const card = payload.data?.content ? JSON.parse(payload.data.content) : null;
sentPatches.push(card);
const text = textFromCard(card);
if (text !== null) sentTexts.push(text);
return {};
},
},
},
},
@@ -91,10 +117,27 @@ export function mockFeishuRuntime(): MockFeishuRuntime {
logger: silentLogger,
sentTexts,
sentCards,
sentPatches,
reactions,
};
return rt;
}
function textFromCard(card: unknown): string | null {
if (typeof card !== "object" || card === null) return null;
const elements = (card as { elements?: unknown }).elements;
if (!Array.isArray(elements)) return null;
const parts: string[] = [];
for (const element of elements) {
if (typeof element !== "object" || element === null) continue;
const text = (element as { text?: unknown }).text;
if (typeof text !== "object" || text === null) continue;
const content = (text as { content?: unknown }).content;
if (typeof content === "string") parts.push(content);
}
return parts.length === 0 ? null : parts.join("\n");
}
export interface MockToolCall {
readonly toolCallId: string;
readonly toolName: string;