
























@@ -1,7 +1,10 @@
11import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
2+import { resetLogger, setLoggerOverride } from "../logging/logger.js";
3+import { loggingState } from "../logging/state.js";
2435type MockRegistryToolEntry = {
46pluginId: string;
7+names?: string[];
58optional: boolean;
69source: string;
710factory: (ctx: unknown) => unknown;
@@ -93,6 +96,7 @@ function setMultiToolRegistry() {
9396function createOptionalDemoEntry(): MockRegistryToolEntry {
9497return {
9598pluginId: "optional-demo",
99+names: ["optional_tool"],
96100optional: true,
97101source: "/tmp/optional-demo.js",
98102factory: () => makeTool("optional_tool"),
@@ -110,6 +114,17 @@ function createMalformedTool(name: string) {
110114};
111115}
112116117+function installConsoleMethodSpy(method: "log" | "warn") {
118+const spy = vi.fn();
119+loggingState.rawConsole = {
120+log: method === "log" ? spy : vi.fn(),
121+info: vi.fn(),
122+warn: method === "warn" ? spy : vi.fn(),
123+error: vi.fn(),
124+};
125+return spy;
126+}
127+113128function resolveWithConflictingCoreName(options?: { suppressNameConflicts?: boolean }) {
114129return resolvePluginTools(
115130createResolveToolsParams({
@@ -227,6 +242,10 @@ describe("resolvePluginTools optional tools", () => {
227242228243afterEach(() => {
229244resetPluginRuntimeStateForTest?.();
245+setLoggerOverride(null);
246+loggingState.rawConsole = null;
247+resetLogger();
248+vi.useRealTimers();
230249});
231250232251it("skips optional tools without explicit allowlist", () => {
@@ -342,6 +361,86 @@ describe("resolvePluginTools optional tools", () => {
342361);
343362});
344363364+it("warns with plugin factory timing details when a factory is slow", () => {
365+vi.useFakeTimers({ now: 0 });
366+const warnSpy = installConsoleMethodSpy("warn");
367+setLoggerOverride({ level: "silent", consoleLevel: "warn" });
368+setRegistry([
369+{
370+pluginId: "optional-demo",
371+names: ["optional_tool"],
372+optional: true,
373+source: "/tmp/optional-demo.js",
374+factory: () => {
375+vi.advanceTimersByTime(1200);
376+return makeTool("optional_tool");
377+},
378+},
379+]);
380+381+const tools = resolveOptionalDemoTools(["optional_tool"]);
382+383+expectResolvedToolNames(tools, ["optional_tool"]);
384+expect(warnSpy).toHaveBeenCalledTimes(1);
385+const message = String(warnSpy.mock.calls[0]?.[0] ?? "");
386+expect(message).toContain("[trace:plugin-tools] factory timings");
387+expect(message).toContain("totalMs=1200");
388+expect(message).toContain("optional-demo:1200ms@1200ms");
389+expect(message).toContain("names=[optional_tool]");
390+expect(message).toContain("result=single");
391+expect(message).toContain("count=1");
392+});
393+394+it("emits trace factory timings below the warn threshold when trace logging is enabled", () => {
395+vi.useFakeTimers({ now: 0 });
396+const logSpy = installConsoleMethodSpy("log");
397+setLoggerOverride({ level: "silent", consoleLevel: "trace" });
398+setRegistry([
399+{
400+pluginId: "optional-demo",
401+names: ["optional_tool"],
402+optional: true,
403+source: "/tmp/optional-demo.js",
404+factory: () => {
405+vi.advanceTimersByTime(5);
406+return makeTool("optional_tool");
407+},
408+},
409+]);
410+411+const tools = resolveOptionalDemoTools(["optional_tool"]);
412+413+expectResolvedToolNames(tools, ["optional_tool"]);
414+expect(logSpy).toHaveBeenCalledTimes(1);
415+const message = String(logSpy.mock.calls[0]?.[0] ?? "");
416+expect(message).toContain("[trace:plugin-tools] factory timings");
417+expect(message).toContain("totalMs=5");
418+expect(message).toContain("optional-demo:5ms@5ms");
419+});
420+421+it("does not log plugin factory timings for fast factories without trace logging", () => {
422+vi.useFakeTimers({ now: 0 });
423+const warnSpy = installConsoleMethodSpy("warn");
424+setLoggerOverride({ level: "silent", consoleLevel: "warn" });
425+setRegistry([
426+{
427+pluginId: "optional-demo",
428+names: ["optional_tool"],
429+optional: true,
430+source: "/tmp/optional-demo.js",
431+factory: () => {
432+vi.advanceTimersByTime(5);
433+return makeTool("optional_tool");
434+},
435+},
436+]);
437+438+const tools = resolveOptionalDemoTools(["optional_tool"]);
439+440+expectResolvedToolNames(tools, ["optional_tool"]);
441+expect(warnSpy).not.toHaveBeenCalled();
442+});
443+345444it("skips allowlisted optional malformed plugin tools", () => {
346445const registry = setRegistry([
347446{
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。