test(memory): cover mcporter qmd search + daemon warm

This commit is contained in:
vignesh07
2026-02-17 17:28:00 -08:00
committed by Vignesh Natarajan
parent 5c3626a21f
commit 422711ecd9
2 changed files with 96 additions and 1 deletions

View File

@@ -921,6 +921,88 @@ describe("QmdMemoryManager", () => {
await manager.close();
});
it("runs qmd searches via mcporter and warns when startDaemon=false", async () => {
cfg = {
...cfg,
memory: {
backend: "qmd",
qmd: {
includeDefaultMemory: false,
update: { interval: "0s", debounceMs: 60_000, onBoot: false },
paths: [{ path: workspaceDir, pattern: "**/*.md", name: "workspace" }],
mcporter: { enabled: true, serverName: "qmd", startDaemon: false },
},
},
} as OpenClawConfig;
spawnMock.mockImplementation((cmd: string, args: string[]) => {
const child = createMockChild({ autoClose: false });
if (cmd === "mcporter" && args[0] === "call") {
emitAndClose(child, "stdout", JSON.stringify({ results: [] }));
return child;
}
emitAndClose(child, "stdout", "[]");
return child;
});
const { manager } = await createManager();
logWarnMock.mockClear();
await expect(
manager.search("hello", { sessionKey: "agent:main:slack:dm:u123" }),
).resolves.toEqual([]);
const mcporterCalls = spawnMock.mock.calls.filter((call: unknown[]) => call[0] === "mcporter");
expect(mcporterCalls.length).toBeGreaterThan(0);
expect(mcporterCalls.some((call: unknown[]) => (call[1] as string[])[0] === "daemon")).toBe(
false,
);
expect(logWarnMock).toHaveBeenCalledWith(expect.stringContaining("cold-start"));
await manager.close();
});
it("starts the mcporter daemon only once when enabled", async () => {
cfg = {
...cfg,
memory: {
backend: "qmd",
qmd: {
includeDefaultMemory: false,
update: { interval: "0s", debounceMs: 60_000, onBoot: false },
paths: [{ path: workspaceDir, pattern: "**/*.md", name: "workspace" }],
mcporter: { enabled: true, serverName: "qmd", startDaemon: true },
},
},
} as OpenClawConfig;
spawnMock.mockImplementation((cmd: string, args: string[]) => {
const child = createMockChild({ autoClose: false });
if (cmd === "mcporter" && args[0] === "daemon") {
emitAndClose(child, "stdout", "");
return child;
}
if (cmd === "mcporter" && args[0] === "call") {
emitAndClose(child, "stdout", JSON.stringify({ results: [] }));
return child;
}
emitAndClose(child, "stdout", "[]");
return child;
});
const { manager } = await createManager();
await manager.search("one", { sessionKey: "agent:main:slack:dm:u123" });
await manager.search("two", { sessionKey: "agent:main:slack:dm:u123" });
const daemonStarts = spawnMock.mock.calls.filter(
(call: unknown[]) => call[0] === "mcporter" && (call[1] as string[])[0] === "daemon",
);
expect(daemonStarts).toHaveLength(1);
await manager.close();
});
it("fails closed when no managed collections are configured", async () => {
cfg = {
...cfg,

View File

@@ -802,7 +802,20 @@ export class QmdMemoryManager implements MemorySearchManager {
}
private async ensureMcporterDaemonStarted(mcporter: ResolvedQmdMcporterConfig): Promise<void> {
if (!mcporter.enabled || !mcporter.startDaemon) {
if (!mcporter.enabled) {
return;
}
if (!mcporter.startDaemon) {
type McporterWarnGlobal = typeof globalThis & {
__openclawMcporterColdStartWarned?: boolean;
};
const g: McporterWarnGlobal = globalThis;
if (!g.__openclawMcporterColdStartWarned) {
g.__openclawMcporterColdStartWarned = true;
log.warn(
"mcporter qmd bridge enabled but startDaemon=false; each query may cold-start QMD MCP. Consider setting memory.qmd.mcporter.startDaemon=true to keep it warm.",
);
}
return;
}
type McporterGlobal = typeof globalThis & {