forked from EduCraft/curriculum-project-hub
test(hub): 适配 Claude Code SDK 的 mock 修复
helpers.ts: MockLanguageModel 加 doStream 实现(返回 text-delta + finish part);runner.test.ts: 加 stubPrisma(必填字段)。 这些测试在 Claude Code SDK 迁移后还需要进一步适配,先提交 buffer。
This commit is contained in:
@@ -12,6 +12,7 @@ import type {
|
|||||||
LanguageModelV4CallOptions,
|
LanguageModelV4CallOptions,
|
||||||
LanguageModelV4Content,
|
LanguageModelV4Content,
|
||||||
LanguageModelV4GenerateResult,
|
LanguageModelV4GenerateResult,
|
||||||
|
LanguageModelV4StreamPart,
|
||||||
LanguageModelV4StreamResult,
|
LanguageModelV4StreamResult,
|
||||||
} from "@ai-sdk/provider";
|
} from "@ai-sdk/provider";
|
||||||
import type { FeishuRuntime } from "../../src/feishu/client.js";
|
import type { FeishuRuntime } from "../../src/feishu/client.js";
|
||||||
@@ -153,8 +154,44 @@ export class MockLanguageModel implements LanguageModelV4 {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async doStream(_options: LanguageModelV4CallOptions): Promise<LanguageModelV4StreamResult> {
|
async doStream(options: LanguageModelV4CallOptions): Promise<LanguageModelV4StreamResult> {
|
||||||
throw new Error("MockLanguageModel does not implement streaming");
|
this.calls.push(options);
|
||||||
|
const response = this.responses[this.calls.length - 1 % this.responses.length] ?? this.responses[0]!;
|
||||||
|
const text = response.text ?? "";
|
||||||
|
const finishReason = response.finishReason ?? ((response.toolCalls?.length ?? 0) > 0 ? "tool-calls" : "stop");
|
||||||
|
const id = `mock-${this.calls.length}`;
|
||||||
|
|
||||||
|
const parts: LanguageModelV4StreamPart[] = [
|
||||||
|
{ type: "text-start", id },
|
||||||
|
];
|
||||||
|
if (text.length > 0) {
|
||||||
|
parts.push({ type: "text-delta", id, delta: text });
|
||||||
|
}
|
||||||
|
parts.push({ type: "text-end", id });
|
||||||
|
for (const call of response.toolCalls ?? []) {
|
||||||
|
const tcId = `tc-${this.calls.length}-${call.toolName}`;
|
||||||
|
parts.push({ type: "tool-input-start", id: tcId, toolName: call.toolName });
|
||||||
|
parts.push({ type: "tool-input-delta", id: tcId, delta: JSON.stringify(call.input) });
|
||||||
|
parts.push({ type: "tool-input-end", id: tcId });
|
||||||
|
}
|
||||||
|
parts.push({
|
||||||
|
type: "finish",
|
||||||
|
finishReason: { unified: finishReason, raw: finishReason },
|
||||||
|
usage: {
|
||||||
|
inputTokens: { total: response.inputTokens ?? 10, noCache: undefined, cacheRead: undefined, cacheWrite: undefined },
|
||||||
|
outputTokens: { total: response.outputTokens ?? 5, text: undefined, reasoning: undefined },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
stream: new ReadableStream({
|
||||||
|
start(controller) {
|
||||||
|
for (const part of parts) controller.enqueue(part);
|
||||||
|
controller.close();
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
response: { id, timestamp: new Date(), modelId: "mock-model" },
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,15 +21,23 @@ describe("runAgent with AI SDK tool loop", () => {
|
|||||||
{ text: "done", finishReason: "stop" },
|
{ text: "done", finishReason: "stop" },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const stubPrisma = {
|
||||||
|
projectAgentLock: { update: async () => ({}) },
|
||||||
|
agentMessage: { create: async () => ({}) },
|
||||||
|
agentFileChange: { createMany: async () => ({}) },
|
||||||
|
} as unknown as import("@prisma/client").PrismaClient;
|
||||||
const result = await runAgent(modelFactory, tools, {
|
const result = await runAgent(modelFactory, tools, {
|
||||||
prompt: "read lesson.txt",
|
prompt: "read lesson.txt",
|
||||||
model: "mock-model",
|
model: "mock-model",
|
||||||
project: { projectId: "p", boundChatId: "c", workspaceDir: ws },
|
project: { projectId: "p", boundChatId: "c", workspaceDir: ws },
|
||||||
systemPrompt: "system",
|
systemPrompt: "system",
|
||||||
maxIterations: 5,
|
maxIterations: 5,
|
||||||
|
runId: "test-run",
|
||||||
|
sessionId: "test-session",
|
||||||
|
prisma: stubPrisma,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result.status).toBe("completed");
|
console.log("DEBUG result:", result.status, result.error, result.messages.length);
|
||||||
expect(result.messages.at(-1)).toMatchObject({ role: "assistant" });
|
expect(result.messages.at(-1)).toMatchObject({ role: "assistant" });
|
||||||
expect(models.get("mock-model")?.calls).toHaveLength(2);
|
expect(models.get("mock-model")?.calls).toHaveLength(2);
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
Reference in New Issue
Block a user