mirror of
https://github.com/moltbot/moltbot.git
synced 2026-03-07 22:44:16 +00:00
test(memory): share memory-tool manager mock fixture
This commit is contained in:
@@ -1,51 +1,12 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
resetMemoryToolMockState,
|
||||
setMemoryBackend,
|
||||
setMemoryReadFileImpl,
|
||||
setMemorySearchImpl,
|
||||
type MemoryReadParams,
|
||||
} from "../../../test/helpers/memory-tool-manager-mock.js";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
|
||||
let backend: "builtin" | "qmd" = "builtin";
|
||||
let searchImpl: () => Promise<unknown[]> = async () => [
|
||||
{
|
||||
path: "MEMORY.md",
|
||||
startLine: 5,
|
||||
endLine: 7,
|
||||
score: 0.9,
|
||||
snippet: "@@ -5,3 @@\nAssistant: noted",
|
||||
source: "memory" as const,
|
||||
},
|
||||
];
|
||||
type MemoryReadParams = { relPath: string; from?: number; lines?: number };
|
||||
type MemoryReadResult = { text: string; path: string };
|
||||
let readFileImpl: (params: MemoryReadParams) => Promise<MemoryReadResult> = async (params) => ({
|
||||
text: "",
|
||||
path: params.relPath,
|
||||
});
|
||||
|
||||
const stubManager = {
|
||||
search: vi.fn(async () => await searchImpl()),
|
||||
readFile: vi.fn(async (params: MemoryReadParams) => await readFileImpl(params)),
|
||||
status: () => ({
|
||||
backend,
|
||||
files: 1,
|
||||
chunks: 1,
|
||||
dirty: false,
|
||||
workspaceDir: "/workspace",
|
||||
dbPath: "/workspace/.memory/index.sqlite",
|
||||
provider: "builtin",
|
||||
model: "builtin",
|
||||
requestedProvider: "builtin",
|
||||
sources: ["memory" as const],
|
||||
sourceCounts: [{ source: "memory" as const, files: 1, chunks: 1 }],
|
||||
}),
|
||||
sync: vi.fn(),
|
||||
probeVectorAvailability: vi.fn(async () => true),
|
||||
close: vi.fn(),
|
||||
};
|
||||
|
||||
vi.mock("../../memory/index.js", () => {
|
||||
return {
|
||||
getMemorySearchManager: async () => ({ manager: stubManager }),
|
||||
};
|
||||
});
|
||||
|
||||
import { createMemoryGetTool, createMemorySearchTool } from "./memory-tool.js";
|
||||
|
||||
function asOpenClawConfig(config: Partial<OpenClawConfig>): OpenClawConfig {
|
||||
@@ -53,24 +14,25 @@ function asOpenClawConfig(config: Partial<OpenClawConfig>): OpenClawConfig {
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
backend = "builtin";
|
||||
searchImpl = async () => [
|
||||
{
|
||||
path: "MEMORY.md",
|
||||
startLine: 5,
|
||||
endLine: 7,
|
||||
score: 0.9,
|
||||
snippet: "@@ -5,3 @@\nAssistant: noted",
|
||||
source: "memory" as const,
|
||||
},
|
||||
];
|
||||
readFileImpl = async (params: MemoryReadParams) => ({ text: "", path: params.relPath });
|
||||
vi.clearAllMocks();
|
||||
resetMemoryToolMockState({
|
||||
backend: "builtin",
|
||||
searchImpl: async () => [
|
||||
{
|
||||
path: "MEMORY.md",
|
||||
startLine: 5,
|
||||
endLine: 7,
|
||||
score: 0.9,
|
||||
snippet: "@@ -5,3 @@\nAssistant: noted",
|
||||
source: "memory" as const,
|
||||
},
|
||||
],
|
||||
readFileImpl: async (params: MemoryReadParams) => ({ text: "", path: params.relPath }),
|
||||
});
|
||||
});
|
||||
|
||||
describe("memory search citations", () => {
|
||||
it("appends source information when citations are enabled", async () => {
|
||||
backend = "builtin";
|
||||
setMemoryBackend("builtin");
|
||||
const cfg = asOpenClawConfig({
|
||||
memory: { citations: "on" },
|
||||
agents: { list: [{ id: "main", default: true }] },
|
||||
@@ -86,7 +48,7 @@ describe("memory search citations", () => {
|
||||
});
|
||||
|
||||
it("leaves snippet untouched when citations are off", async () => {
|
||||
backend = "builtin";
|
||||
setMemoryBackend("builtin");
|
||||
const cfg = asOpenClawConfig({
|
||||
memory: { citations: "off" },
|
||||
agents: { list: [{ id: "main", default: true }] },
|
||||
@@ -102,7 +64,7 @@ describe("memory search citations", () => {
|
||||
});
|
||||
|
||||
it("clamps decorated snippets to qmd injected budget", async () => {
|
||||
backend = "qmd";
|
||||
setMemoryBackend("qmd");
|
||||
const cfg = asOpenClawConfig({
|
||||
memory: { citations: "on", backend: "qmd", qmd: { limits: { maxInjectedChars: 20 } } },
|
||||
agents: { list: [{ id: "main", default: true }] },
|
||||
@@ -117,7 +79,7 @@ describe("memory search citations", () => {
|
||||
});
|
||||
|
||||
it("honors auto mode for direct chats", async () => {
|
||||
backend = "builtin";
|
||||
setMemoryBackend("builtin");
|
||||
const cfg = asOpenClawConfig({
|
||||
memory: { citations: "auto" },
|
||||
agents: { list: [{ id: "main", default: true }] },
|
||||
@@ -135,7 +97,7 @@ describe("memory search citations", () => {
|
||||
});
|
||||
|
||||
it("suppresses citations for auto mode in group chats", async () => {
|
||||
backend = "builtin";
|
||||
setMemoryBackend("builtin");
|
||||
const cfg = asOpenClawConfig({
|
||||
memory: { citations: "auto" },
|
||||
agents: { list: [{ id: "main", default: true }] },
|
||||
@@ -155,9 +117,9 @@ describe("memory search citations", () => {
|
||||
|
||||
describe("memory tools", () => {
|
||||
it("does not throw when memory_search fails (e.g. embeddings 429)", async () => {
|
||||
searchImpl = async () => {
|
||||
setMemorySearchImpl(async () => {
|
||||
throw new Error("openai embeddings failed: 429 insufficient_quota");
|
||||
};
|
||||
});
|
||||
|
||||
const cfg = { agents: { list: [{ id: "main", default: true }] } };
|
||||
const tool = createMemorySearchTool({ config: cfg });
|
||||
@@ -178,9 +140,9 @@ describe("memory tools", () => {
|
||||
});
|
||||
|
||||
it("does not throw when memory_get fails", async () => {
|
||||
readFileImpl = async (_params: MemoryReadParams) => {
|
||||
setMemoryReadFileImpl(async (_params: MemoryReadParams) => {
|
||||
throw new Error("path required");
|
||||
};
|
||||
});
|
||||
|
||||
const cfg = { agents: { list: [{ id: "main", default: true }] } };
|
||||
const tool = createMemoryGetTool({ config: cfg });
|
||||
@@ -199,9 +161,9 @@ describe("memory tools", () => {
|
||||
});
|
||||
|
||||
it("returns empty text without error when file does not exist (ENOENT)", async () => {
|
||||
readFileImpl = async (_params: MemoryReadParams) => {
|
||||
setMemoryReadFileImpl(async (_params: MemoryReadParams) => {
|
||||
return { text: "", path: "memory/2026-02-19.md" };
|
||||
};
|
||||
});
|
||||
|
||||
const cfg = { agents: { list: [{ id: "main", default: true }] } };
|
||||
const tool = createMemoryGetTool({ config: cfg });
|
||||
|
||||
@@ -1,45 +1,19 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
type SearchImpl = () => Promise<unknown[]>;
|
||||
let searchImpl: SearchImpl = async () => [];
|
||||
|
||||
const stubManager = {
|
||||
search: vi.fn(async () => await searchImpl()),
|
||||
readFile: vi.fn(),
|
||||
status: () => ({
|
||||
backend: "builtin" as const,
|
||||
files: 1,
|
||||
chunks: 1,
|
||||
dirty: false,
|
||||
workspaceDir: "/workspace",
|
||||
dbPath: "/workspace/.memory/index.sqlite",
|
||||
provider: "builtin",
|
||||
model: "builtin",
|
||||
requestedProvider: "builtin",
|
||||
sources: ["memory" as const],
|
||||
sourceCounts: [{ source: "memory" as const, files: 1, chunks: 1 }],
|
||||
}),
|
||||
sync: vi.fn(),
|
||||
probeVectorAvailability: vi.fn(async () => true),
|
||||
close: vi.fn(),
|
||||
};
|
||||
|
||||
vi.mock("../../memory/index.js", () => ({
|
||||
getMemorySearchManager: async () => ({ manager: stubManager }),
|
||||
}));
|
||||
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
resetMemoryToolMockState,
|
||||
setMemorySearchImpl,
|
||||
} from "../../../test/helpers/memory-tool-manager-mock.js";
|
||||
import { createMemorySearchTool } from "./memory-tool.js";
|
||||
|
||||
describe("memory_search unavailable payloads", () => {
|
||||
beforeEach(() => {
|
||||
searchImpl = async () => [];
|
||||
vi.clearAllMocks();
|
||||
resetMemoryToolMockState({ searchImpl: async () => [] });
|
||||
});
|
||||
|
||||
it("returns explicit unavailable metadata for quota failures", async () => {
|
||||
searchImpl = async () => {
|
||||
setMemorySearchImpl(async () => {
|
||||
throw new Error("openai embeddings failed: 429 insufficient_quota");
|
||||
};
|
||||
});
|
||||
|
||||
const tool = createMemorySearchTool({
|
||||
config: { agents: { list: [{ id: "main", default: true }] } },
|
||||
@@ -60,9 +34,9 @@ describe("memory_search unavailable payloads", () => {
|
||||
});
|
||||
|
||||
it("returns explicit unavailable metadata for non-quota failures", async () => {
|
||||
searchImpl = async () => {
|
||||
setMemorySearchImpl(async () => {
|
||||
throw new Error("embedding provider timeout");
|
||||
};
|
||||
});
|
||||
|
||||
const tool = createMemorySearchTool({
|
||||
config: { agents: { list: [{ id: "main", default: true }] } },
|
||||
|
||||
65
test/helpers/memory-tool-manager-mock.ts
Normal file
65
test/helpers/memory-tool-manager-mock.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { vi } from "vitest";
|
||||
|
||||
export type SearchImpl = () => Promise<unknown[]>;
|
||||
export type MemoryReadParams = { relPath: string; from?: number; lines?: number };
|
||||
export type MemoryReadResult = { text: string; path: string };
|
||||
type MemoryBackend = "builtin" | "qmd";
|
||||
|
||||
let backend: MemoryBackend = "builtin";
|
||||
let searchImpl: SearchImpl = async () => [];
|
||||
let readFileImpl: (params: MemoryReadParams) => Promise<MemoryReadResult> = async (params) => ({
|
||||
text: "",
|
||||
path: params.relPath,
|
||||
});
|
||||
|
||||
const stubManager = {
|
||||
search: vi.fn(async () => await searchImpl()),
|
||||
readFile: vi.fn(async (params: MemoryReadParams) => await readFileImpl(params)),
|
||||
status: () => ({
|
||||
backend,
|
||||
files: 1,
|
||||
chunks: 1,
|
||||
dirty: false,
|
||||
workspaceDir: "/workspace",
|
||||
dbPath: "/workspace/.memory/index.sqlite",
|
||||
provider: "builtin",
|
||||
model: "builtin",
|
||||
requestedProvider: "builtin",
|
||||
sources: ["memory" as const],
|
||||
sourceCounts: [{ source: "memory" as const, files: 1, chunks: 1 }],
|
||||
}),
|
||||
sync: vi.fn(),
|
||||
probeVectorAvailability: vi.fn(async () => true),
|
||||
close: vi.fn(),
|
||||
};
|
||||
|
||||
vi.mock("../../src/memory/index.js", () => ({
|
||||
getMemorySearchManager: async () => ({ manager: stubManager }),
|
||||
}));
|
||||
|
||||
export function setMemoryBackend(next: MemoryBackend): void {
|
||||
backend = next;
|
||||
}
|
||||
|
||||
export function setMemorySearchImpl(next: SearchImpl): void {
|
||||
searchImpl = next;
|
||||
}
|
||||
|
||||
export function setMemoryReadFileImpl(
|
||||
next: (params: MemoryReadParams) => Promise<MemoryReadResult>,
|
||||
): void {
|
||||
readFileImpl = next;
|
||||
}
|
||||
|
||||
export function resetMemoryToolMockState(overrides?: {
|
||||
backend?: MemoryBackend;
|
||||
searchImpl?: SearchImpl;
|
||||
readFileImpl?: (params: MemoryReadParams) => Promise<MemoryReadResult>;
|
||||
}): void {
|
||||
backend = overrides?.backend ?? "builtin";
|
||||
searchImpl = overrides?.searchImpl ?? (async () => []);
|
||||
readFileImpl =
|
||||
overrides?.readFileImpl ??
|
||||
(async (params: MemoryReadParams) => ({ text: "", path: params.relPath }));
|
||||
vi.clearAllMocks();
|
||||
}
|
||||
Reference in New Issue
Block a user