forked from EduCraft/curriculum-project-hub
fix: reply to Feishu trigger messages
This commit is contained in:
@@ -62,6 +62,12 @@ export interface MockFeishuRuntime extends FeishuRuntime {
|
||||
readonly sentTexts: string[];
|
||||
readonly sentCards: unknown[];
|
||||
readonly sentPatches: unknown[];
|
||||
readonly sentReplies: Array<{
|
||||
readonly messageId: string;
|
||||
readonly msgType: string;
|
||||
readonly content: string;
|
||||
readonly replyInThread: unknown;
|
||||
}>;
|
||||
readonly reactions: Array<{ readonly messageId: string; readonly emoji: string }>;
|
||||
readonly readableMessages: Map<string, MockFeishuMessage>;
|
||||
readonly threadMessages: Map<string, readonly MockFeishuMessage[]>;
|
||||
@@ -84,6 +90,7 @@ export function mockFeishuRuntime(): MockFeishuRuntime {
|
||||
const sentTexts: string[] = [];
|
||||
const sentCards: unknown[] = [];
|
||||
const sentPatches: unknown[] = [];
|
||||
const sentReplies: MockFeishuRuntime["sentReplies"] = [];
|
||||
const reactions: Array<{ messageId: string; emoji: string }> = [];
|
||||
const readableMessages = new Map<string, MockFeishuMessage>();
|
||||
const threadMessages = new Map<string, readonly MockFeishuMessage[]>();
|
||||
@@ -108,19 +115,21 @@ export function mockFeishuRuntime(): MockFeishuRuntime {
|
||||
message: {
|
||||
create: async (p: unknown) => {
|
||||
const payload = p as { data?: { msg_type?: string; content?: string } };
|
||||
if (payload.data?.msg_type === "interactive") {
|
||||
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 };
|
||||
sentTexts.push(c.text ?? payload.data?.content ?? "");
|
||||
} catch {
|
||||
sentTexts.push(payload.data?.content ?? "");
|
||||
}
|
||||
}
|
||||
recordSentMessage(payload.data, sentTexts, sentCards);
|
||||
return { data: { message_id: "mock-msg-id" } };
|
||||
},
|
||||
reply: async (p: unknown) => {
|
||||
const payload = p as {
|
||||
path?: { message_id?: string };
|
||||
data?: { msg_type?: string; content?: string; reply_in_thread?: unknown };
|
||||
};
|
||||
sentReplies.push({
|
||||
messageId: payload.path?.message_id ?? "",
|
||||
msgType: payload.data?.msg_type ?? "",
|
||||
content: payload.data?.content ?? "",
|
||||
replyInThread: payload.data?.reply_in_thread,
|
||||
});
|
||||
recordSentMessage(payload.data, sentTexts, sentCards);
|
||||
return { data: { message_id: "mock-msg-id" } };
|
||||
},
|
||||
patch: async (p: unknown) => {
|
||||
@@ -153,6 +162,7 @@ export function mockFeishuRuntime(): MockFeishuRuntime {
|
||||
sentTexts,
|
||||
sentCards,
|
||||
sentPatches,
|
||||
sentReplies,
|
||||
reactions,
|
||||
readableMessages,
|
||||
threadMessages,
|
||||
@@ -160,6 +170,27 @@ export function mockFeishuRuntime(): MockFeishuRuntime {
|
||||
return rt;
|
||||
}
|
||||
|
||||
function recordSentMessage(
|
||||
data: { msg_type?: string; content?: string } | undefined,
|
||||
sentTexts: string[],
|
||||
sentCards: unknown[],
|
||||
): void {
|
||||
if (data?.msg_type === "interactive") {
|
||||
const card = data.content ? JSON.parse(data.content) : null;
|
||||
sentCards.push(card);
|
||||
const text = textFromCard(card);
|
||||
if (text !== null) sentTexts.push(text);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const c = JSON.parse(data?.content ?? "{}") as { text?: string };
|
||||
sentTexts.push(c.text ?? data?.content ?? "");
|
||||
} catch {
|
||||
sentTexts.push(data?.content ?? "");
|
||||
}
|
||||
}
|
||||
|
||||
function textFromCard(card: unknown): string | null {
|
||||
if (typeof card !== "object" || card === null) return null;
|
||||
const elements = (card as { elements?: unknown }).elements;
|
||||
|
||||
@@ -104,8 +104,9 @@ describe("trigger full lifecycle (integration)", () => {
|
||||
it("creates a run, acquires + releases the lock, sends status card", async () => {
|
||||
await seedProject("proj-1", "chat-1");
|
||||
const trigger = makeTriggerHandler({ prisma, settings, logger: silentLogger, runAgent, messageBatcherOptions: { maxMessages: 1 } });
|
||||
const event = makeEvent("chat-1", "@_user_1 写教案");
|
||||
|
||||
await trigger(makeEvent("chat-1", "@_user_1 写教案"), rt);
|
||||
await trigger(event, rt);
|
||||
|
||||
// Wait for the async run to complete (fire-and-forget in trigger).
|
||||
await vi.waitFor(async () => {
|
||||
@@ -122,6 +123,11 @@ describe("trigger full lifecycle (integration)", () => {
|
||||
|
||||
// A status card was sent.
|
||||
expect(rt.sentCards.length).toBeGreaterThanOrEqual(1);
|
||||
expect(rt.sentReplies[0]).toMatchObject({
|
||||
messageId: event.message.message_id,
|
||||
msgType: "interactive",
|
||||
replyInThread: undefined,
|
||||
});
|
||||
expect(rt.sentTexts).toContain("mock response");
|
||||
expect(runAgentCalls).toHaveLength(1);
|
||||
expect(runAgentCalls[0]?.providerEnv).toMatchObject({
|
||||
|
||||
Reference in New Issue
Block a user