feat: add import and event commands, refactor CLI, remove baseUrl config, update docs (#4704)
This commit is contained in:
@@ -58,6 +58,8 @@ interface MockCommand {
|
||||
_args: Array<{ name: string; desc: string }>;
|
||||
command(name: string): MockCommand;
|
||||
description(desc: string): MockCommand;
|
||||
configureHelp(opts: any): MockCommand;
|
||||
hook(event: string, fn: (...args: any[]) => any): MockCommand;
|
||||
option(flags: string, desc: string, defaultVal?: string): MockCommand;
|
||||
argument(name: string, desc: string): MockCommand;
|
||||
action(fn: (...args: any[]) => any): MockCommand;
|
||||
@@ -80,6 +82,12 @@ function createMockCommand(name: string): MockCommand {
|
||||
cmd._description = desc;
|
||||
return cmd;
|
||||
},
|
||||
configureHelp(_opts: any) {
|
||||
return cmd;
|
||||
},
|
||||
hook(_event: string, _fn: (...args: any[]) => any) {
|
||||
return cmd;
|
||||
},
|
||||
option(flags: string, desc: string, defaultVal?: string) {
|
||||
cmd._options.push({ flags, desc, defaultVal });
|
||||
return cmd;
|
||||
@@ -145,6 +153,18 @@ function createMockBackend() {
|
||||
connected: true,
|
||||
url: "https://api.mem0.ai",
|
||||
}),
|
||||
add: vi.fn().mockResolvedValue({ id: "new-1" }),
|
||||
listEvents: vi.fn().mockResolvedValue([
|
||||
{ id: "evt-1111-2222-3333-4444", event_type: "ADD", status: "SUCCEEDED", latency: 1500, created_at: "2026-04-01T12:00:00Z" },
|
||||
]),
|
||||
getEvent: vi.fn().mockResolvedValue({
|
||||
id: "evt-1111-2222-3333-4444",
|
||||
event_type: "ADD",
|
||||
status: "SUCCEEDED",
|
||||
latency: 1500,
|
||||
created_at: "2026-04-01T12:00:00Z",
|
||||
updated_at: "2026-04-01T12:00:01Z",
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -284,7 +304,6 @@ describe("registerCliCommands", () => {
|
||||
expect(names).toContain("list");
|
||||
expect(names).toContain("update");
|
||||
expect(names).toContain("delete");
|
||||
expect(names).toContain("history");
|
||||
expect(names).toContain("status");
|
||||
expect(names).toContain("config");
|
||||
expect(names).toContain("dream");
|
||||
@@ -321,7 +340,6 @@ describe("registerCliCommands", () => {
|
||||
expect(writePluginAuth).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
apiKey: "m0-my-key-1234",
|
||||
baseUrl: "https://api.mem0.ai",
|
||||
mode: "platform",
|
||||
}),
|
||||
);
|
||||
@@ -857,48 +875,6 @@ describe("registerCliCommands", () => {
|
||||
});
|
||||
});
|
||||
|
||||
// ========================================================================
|
||||
// history subcommand
|
||||
// ========================================================================
|
||||
|
||||
describe("history subcommand", () => {
|
||||
it("calls provider.history and prints entries as JSON", async () => {
|
||||
const { mem0, provider } = setup();
|
||||
const historyCmd = findCommand(mem0, "history")!;
|
||||
|
||||
await historyCmd._action!("m1");
|
||||
|
||||
expect(provider.history).toHaveBeenCalledWith("m1");
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||
expect.stringContaining("UPDATE"),
|
||||
);
|
||||
});
|
||||
|
||||
it("prints message when no history found", async () => {
|
||||
const { mem0, provider } = setup();
|
||||
provider.history.mockResolvedValueOnce([]);
|
||||
const historyCmd = findCommand(mem0, "history")!;
|
||||
|
||||
await historyCmd._action!("m1");
|
||||
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||
"No history found for this memory.",
|
||||
);
|
||||
});
|
||||
|
||||
it("handles history errors gracefully", async () => {
|
||||
const { mem0, provider } = setup();
|
||||
provider.history.mockRejectedValueOnce(new Error("history boom"));
|
||||
const historyCmd = findCommand(mem0, "history")!;
|
||||
|
||||
await historyCmd._action!("m1");
|
||||
|
||||
expect(consoleSpy.error).toHaveBeenCalledWith(
|
||||
expect.stringContaining("History failed"),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// ========================================================================
|
||||
// status subcommand
|
||||
// ========================================================================
|
||||
@@ -964,9 +940,9 @@ describe("registerCliCommands", () => {
|
||||
const allOutput = consoleSpy.log.mock.calls.map((c) => c[0]).join("\n");
|
||||
expect(allOutput).toContain("Key");
|
||||
expect(allOutput).toContain("Value");
|
||||
expect(allOutput).toContain("platform.api_key");
|
||||
expect(allOutput).toContain("platform.base_url");
|
||||
expect(allOutput).toContain("defaults.user_id");
|
||||
expect(allOutput).toContain("api_key");
|
||||
expect(allOutput).toContain("email");
|
||||
expect(allOutput).toContain("user_id");
|
||||
expect(allOutput).toContain("Config file:");
|
||||
});
|
||||
});
|
||||
@@ -1026,14 +1002,14 @@ describe("registerCliCommands", () => {
|
||||
expect(logged).not.toContain("supersecret");
|
||||
});
|
||||
|
||||
it("supports dot-notation keys like platform.api_key", () => {
|
||||
it("supports short alias keys like email", () => {
|
||||
const { mem0 } = setup();
|
||||
const configCmd = findCommand(mem0, "config")!;
|
||||
const getCmd = findCommand(configCmd, "get")!;
|
||||
|
||||
getCmd._action!("platform.base_url");
|
||||
getCmd._action!("email");
|
||||
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith("https://api.mem0.ai");
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith("(not set)");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1143,12 +1119,12 @@ describe("registerCliCommands", () => {
|
||||
expect(writePluginAuth).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("supports dot-notation keys for set", () => {
|
||||
it("supports short alias keys for set", () => {
|
||||
const { mem0 } = setup();
|
||||
const configCmd = findCommand(mem0, "config")!;
|
||||
const setCmd = findCommand(configCmd, "set")!;
|
||||
|
||||
setCmd._action!("defaults.user_id", "bob");
|
||||
setCmd._action!("user_id", "bob");
|
||||
|
||||
expect(writePluginAuth).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ userId: "bob" }),
|
||||
@@ -1268,4 +1244,173 @@ describe("registerCliCommands", () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// ========================================================================
|
||||
// import subcommand
|
||||
// ========================================================================
|
||||
|
||||
describe("import subcommand", () => {
|
||||
it("imports memories from a JSON array file", async () => {
|
||||
const { mem0, backend } = setup();
|
||||
const { readText } = await import("../fs-safe.ts");
|
||||
(readText as ReturnType<typeof vi.fn>).mockReturnValueOnce(
|
||||
JSON.stringify([
|
||||
{ memory: "fact one" },
|
||||
{ memory: "fact two" },
|
||||
]),
|
||||
);
|
||||
const importCmd = findCommand(mem0, "import")!;
|
||||
|
||||
await importCmd._action!("memories.json", {});
|
||||
|
||||
expect(backend.add).toHaveBeenCalledTimes(2);
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith("Imported 2 memories.");
|
||||
});
|
||||
|
||||
it("imports a single JSON object", async () => {
|
||||
const { mem0, backend } = setup();
|
||||
const { readText } = await import("../fs-safe.ts");
|
||||
(readText as ReturnType<typeof vi.fn>).mockReturnValueOnce(
|
||||
JSON.stringify({ memory: "single fact" }),
|
||||
);
|
||||
const importCmd = findCommand(mem0, "import")!;
|
||||
|
||||
await importCmd._action!("single.json", {});
|
||||
|
||||
expect(backend.add).toHaveBeenCalledTimes(1);
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith("Imported 1 memories.");
|
||||
});
|
||||
|
||||
it("skips items with no extractable content", async () => {
|
||||
const { mem0, backend } = setup();
|
||||
const { readText } = await import("../fs-safe.ts");
|
||||
(readText as ReturnType<typeof vi.fn>).mockReturnValueOnce(
|
||||
JSON.stringify([{ memory: "valid" }, { nofield: true }]),
|
||||
);
|
||||
const importCmd = findCommand(mem0, "import")!;
|
||||
|
||||
await importCmd._action!("mixed.json", {});
|
||||
|
||||
expect(backend.add).toHaveBeenCalledTimes(1);
|
||||
expect(consoleSpy.error).toHaveBeenCalledWith("1 memories failed to import.");
|
||||
});
|
||||
|
||||
it("uses --user-id and --agent-id overrides", async () => {
|
||||
const { mem0, backend } = setup();
|
||||
const { readText } = await import("../fs-safe.ts");
|
||||
(readText as ReturnType<typeof vi.fn>).mockReturnValueOnce(
|
||||
JSON.stringify([{ memory: "fact" }]),
|
||||
);
|
||||
const importCmd = findCommand(mem0, "import")!;
|
||||
|
||||
await importCmd._action!("f.json", { userId: "override-user", agentId: "agent-1" });
|
||||
|
||||
expect(backend.add).toHaveBeenCalledWith("fact", undefined, expect.objectContaining({
|
||||
userId: "override-user",
|
||||
agentId: "agent-1",
|
||||
}));
|
||||
});
|
||||
|
||||
it("handles file read errors gracefully", async () => {
|
||||
const { mem0 } = setup();
|
||||
const { readText } = await import("../fs-safe.ts");
|
||||
(readText as ReturnType<typeof vi.fn>).mockImplementationOnce(() => {
|
||||
throw new Error("ENOENT");
|
||||
});
|
||||
const importCmd = findCommand(mem0, "import")!;
|
||||
|
||||
await importCmd._action!("missing.json", {});
|
||||
|
||||
expect(consoleSpy.error).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Failed to read file"),
|
||||
);
|
||||
});
|
||||
|
||||
it("handles backend.add failures gracefully", async () => {
|
||||
const { mem0, backend } = setup();
|
||||
const { readText } = await import("../fs-safe.ts");
|
||||
(readText as ReturnType<typeof vi.fn>).mockReturnValueOnce(
|
||||
JSON.stringify([{ memory: "will fail" }]),
|
||||
);
|
||||
backend.add.mockRejectedValueOnce(new Error("API error"));
|
||||
const importCmd = findCommand(mem0, "import")!;
|
||||
|
||||
await importCmd._action!("fail.json", {});
|
||||
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith("Imported 0 memories.");
|
||||
expect(consoleSpy.error).toHaveBeenCalledWith("1 memories failed to import.");
|
||||
});
|
||||
});
|
||||
|
||||
// ========================================================================
|
||||
// event subcommand
|
||||
// ========================================================================
|
||||
|
||||
describe("event subcommand", () => {
|
||||
it("lists events in table format", async () => {
|
||||
const { mem0, backend } = setup();
|
||||
const eventCmd = findCommand(mem0, "event")!;
|
||||
const listCmd = findCommand(eventCmd, "list")!;
|
||||
|
||||
await listCmd._action!();
|
||||
|
||||
expect(backend.listEvents).toHaveBeenCalled();
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||
expect.stringContaining("evt-1111-2222-3333-4444"),
|
||||
);
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
||||
expect.stringContaining("1 event"),
|
||||
);
|
||||
});
|
||||
|
||||
it("prints message when no events found", async () => {
|
||||
const { mem0, backend } = setup();
|
||||
backend.listEvents.mockResolvedValueOnce([]);
|
||||
const eventCmd = findCommand(mem0, "event")!;
|
||||
const listCmd = findCommand(eventCmd, "list")!;
|
||||
|
||||
await listCmd._action!();
|
||||
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith("No events found.");
|
||||
});
|
||||
|
||||
it("handles event list errors gracefully", async () => {
|
||||
const { mem0, backend } = setup();
|
||||
backend.listEvents.mockRejectedValueOnce(new Error("event boom"));
|
||||
const eventCmd = findCommand(mem0, "event")!;
|
||||
const listCmd = findCommand(eventCmd, "list")!;
|
||||
|
||||
await listCmd._action!();
|
||||
|
||||
expect(consoleSpy.error).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Failed to list events"),
|
||||
);
|
||||
});
|
||||
|
||||
it("shows event status details", async () => {
|
||||
const { mem0, backend } = setup();
|
||||
const eventCmd = findCommand(mem0, "event")!;
|
||||
const statusCmd = findCommand(eventCmd, "status")!;
|
||||
|
||||
await statusCmd._action!("evt-1111-2222-3333-4444");
|
||||
|
||||
expect(backend.getEvent).toHaveBeenCalledWith("evt-1111-2222-3333-4444");
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith("Event ID: evt-1111-2222-3333-4444");
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith("Type: ADD");
|
||||
expect(consoleSpy.log).toHaveBeenCalledWith("Status: SUCCEEDED");
|
||||
});
|
||||
|
||||
it("handles event status errors gracefully", async () => {
|
||||
const { mem0, backend } = setup();
|
||||
backend.getEvent.mockRejectedValueOnce(new Error("not found"));
|
||||
const eventCmd = findCommand(mem0, "event")!;
|
||||
const statusCmd = findCommand(eventCmd, "status")!;
|
||||
|
||||
await statusCmd._action!("bad-id");
|
||||
|
||||
expect(consoleSpy.error).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Failed to get event"),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
||||
import { bootstrapTelemetryFlag } from "../fs-safe.ts";
|
||||
|
||||
describe("bootstrapTelemetryFlag", () => {
|
||||
const originalEnv = process.env.MEM0_TELEMETRY;
|
||||
|
||||
beforeEach(() => {
|
||||
delete (globalThis as any).__mem0_telemetry_override;
|
||||
delete process.env.MEM0_TELEMETRY;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
delete (globalThis as any).__mem0_telemetry_override;
|
||||
if (originalEnv !== undefined) {
|
||||
process.env.MEM0_TELEMETRY = originalEnv;
|
||||
} else {
|
||||
delete process.env.MEM0_TELEMETRY;
|
||||
}
|
||||
});
|
||||
|
||||
it("sets globalThis override when MEM0_TELEMETRY is set", () => {
|
||||
process.env.MEM0_TELEMETRY = "false";
|
||||
bootstrapTelemetryFlag();
|
||||
expect((globalThis as any).__mem0_telemetry_override).toBe("false");
|
||||
});
|
||||
|
||||
it("does not set globalThis override when MEM0_TELEMETRY is unset", () => {
|
||||
bootstrapTelemetryFlag();
|
||||
expect((globalThis as any).__mem0_telemetry_override).toBeUndefined();
|
||||
});
|
||||
|
||||
it("passes through truthy values", () => {
|
||||
process.env.MEM0_TELEMETRY = "true";
|
||||
bootstrapTelemetryFlag();
|
||||
expect((globalThis as any).__mem0_telemetry_override).toBe("true");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,65 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
|
||||
// Mock config-file before importing telemetry
|
||||
vi.mock("../cli/config-file.ts", () => ({
|
||||
readPluginAuth: vi.fn().mockReturnValue({}),
|
||||
}));
|
||||
|
||||
import { captureEvent, PLUGIN_VERSION } from "../telemetry.ts";
|
||||
import { readPluginAuth } from "../cli/config-file.ts";
|
||||
|
||||
describe("telemetry", () => {
|
||||
let fetchSpy: ReturnType<typeof vi.fn>;
|
||||
|
||||
beforeEach(() => {
|
||||
// Reset telemetry enabled state
|
||||
(globalThis as any).__mem0_telemetry_override = undefined;
|
||||
fetchSpy = vi.fn().mockResolvedValue({ ok: true });
|
||||
vi.stubGlobal("fetch", fetchSpy);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
delete (globalThis as any).__mem0_telemetry_override;
|
||||
});
|
||||
|
||||
it("exports PLUGIN_VERSION", () => {
|
||||
expect(PLUGIN_VERSION).toBe("1.0.4");
|
||||
});
|
||||
|
||||
it("captureEvent does not throw", () => {
|
||||
expect(() => captureEvent("test_event")).not.toThrow();
|
||||
});
|
||||
|
||||
it("captureEvent accepts properties and context", () => {
|
||||
expect(() =>
|
||||
captureEvent("test_event", { key: "val" }, { apiKey: "m0-key", mode: "platform" }),
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
it("captureEvent is silent when telemetry disabled", () => {
|
||||
(globalThis as any).__mem0_telemetry_override = "false";
|
||||
// Force re-evaluation by resetting cached value
|
||||
// Since _telemetryEnabled is module-level, we test indirectly
|
||||
expect(() => captureEvent("test_event")).not.toThrow();
|
||||
});
|
||||
|
||||
it("uses userEmail as distinct ID when available", () => {
|
||||
(readPluginAuth as ReturnType<typeof vi.fn>).mockReturnValueOnce({
|
||||
userEmail: "test@example.com",
|
||||
});
|
||||
expect(() => captureEvent("test_event")).not.toThrow();
|
||||
});
|
||||
|
||||
it("falls back to anonymous-openclaw when no apiKey", () => {
|
||||
(readPluginAuth as ReturnType<typeof vi.fn>).mockReturnValueOnce({});
|
||||
expect(() => captureEvent("test_event", {}, {})).not.toThrow();
|
||||
});
|
||||
|
||||
it("handles readPluginAuth errors gracefully", () => {
|
||||
(readPluginAuth as ReturnType<typeof vi.fn>).mockImplementationOnce(() => {
|
||||
throw new Error("config read failed");
|
||||
});
|
||||
expect(() => captureEvent("test_event")).not.toThrow();
|
||||
});
|
||||
});
|
||||
@@ -15,7 +15,8 @@ import { createMemoryGetTool } from "../tools/memory-get.ts";
|
||||
import { createMemoryDeleteTool } from "../tools/memory-delete.ts";
|
||||
import { createMemoryListTool } from "../tools/memory-list.ts";
|
||||
import { createMemoryUpdateTool } from "../tools/memory-update.ts";
|
||||
import { createMemoryHistoryTool } from "../tools/memory-history.ts";
|
||||
import { createMemoryEventListTool } from "../tools/memory-event-list.ts";
|
||||
import { createMemoryEventStatusTool } from "../tools/memory-event-status.ts";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Mock helper
|
||||
@@ -80,10 +81,10 @@ function createMockToolDeps(overrides = {}): ToolDeps {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe("registerAllTools", () => {
|
||||
it("calls api.registerTool exactly 7 times", () => {
|
||||
it("calls api.registerTool exactly 8 times", () => {
|
||||
const ctx = createMockToolDeps();
|
||||
registerAllTools(ctx);
|
||||
expect(ctx.api.registerTool).toHaveBeenCalledTimes(7);
|
||||
expect(ctx.api.registerTool).toHaveBeenCalledTimes(8);
|
||||
});
|
||||
|
||||
it("registers tools with the correct names", () => {
|
||||
@@ -103,7 +104,8 @@ describe("registerAllTools", () => {
|
||||
"memory_list",
|
||||
"memory_update",
|
||||
"memory_delete",
|
||||
"memory_history",
|
||||
"memory_event_list",
|
||||
"memory_event_status",
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -129,6 +131,8 @@ describe("tool factory shape", () => {
|
||||
{ fn: createMemoryGetTool, expectedName: "memory_get" },
|
||||
{ fn: createMemoryDeleteTool, expectedName: "memory_delete" },
|
||||
{ fn: createMemoryListTool, expectedName: "memory_list" },
|
||||
{ fn: createMemoryEventListTool, expectedName: "memory_event_list" },
|
||||
{ fn: createMemoryEventStatusTool, expectedName: "memory_event_status" },
|
||||
];
|
||||
|
||||
for (const { fn, expectedName } of factories) {
|
||||
@@ -883,115 +887,3 @@ describe("memory_update execute", () => {
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// memory_history execute
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe("memory_history execute", () => {
|
||||
it("returns formatted history", async () => {
|
||||
const historyEntries = [
|
||||
{
|
||||
event: "ADD",
|
||||
created_at: "2026-01-01T00:00:00Z",
|
||||
old_memory: null,
|
||||
new_memory: "Initial memory text",
|
||||
},
|
||||
{
|
||||
event: "UPDATE",
|
||||
created_at: "2026-01-02T00:00:00Z",
|
||||
old_memory: "Initial memory text",
|
||||
new_memory: "Updated memory text",
|
||||
},
|
||||
];
|
||||
const ctx = createMockToolDeps({
|
||||
provider: {
|
||||
search: vi.fn(),
|
||||
add: vi.fn(),
|
||||
getAll: vi.fn(),
|
||||
update: vi.fn(),
|
||||
delete: vi.fn(),
|
||||
get: vi.fn(),
|
||||
history: vi.fn().mockResolvedValue(historyEntries),
|
||||
},
|
||||
});
|
||||
const tool = createMemoryHistoryTool(ctx);
|
||||
|
||||
const result = await tool.execute("call-1", { memoryId: "mem-abc" });
|
||||
|
||||
expect(ctx.provider!.history).toHaveBeenCalledWith("mem-abc");
|
||||
expect(result.content[0].text).toContain("History for memory mem-abc");
|
||||
expect(result.content[0].text).toContain("2 entries");
|
||||
expect(result.content[0].text).toContain("[ADD]");
|
||||
expect(result.content[0].text).toContain("[UPDATE]");
|
||||
expect(result.content[0].text).toContain("Initial memory text");
|
||||
expect(result.content[0].text).toContain("Updated memory text");
|
||||
expect(result.content[0].text).toContain("Old: (none)");
|
||||
expect(result.details.count).toBe(2);
|
||||
expect(result.details.history).toEqual(historyEntries);
|
||||
});
|
||||
|
||||
it("handles empty history", async () => {
|
||||
const ctx = createMockToolDeps({
|
||||
provider: {
|
||||
search: vi.fn(),
|
||||
add: vi.fn(),
|
||||
getAll: vi.fn(),
|
||||
update: vi.fn(),
|
||||
delete: vi.fn(),
|
||||
get: vi.fn(),
|
||||
history: vi.fn().mockResolvedValue([]),
|
||||
},
|
||||
});
|
||||
const tool = createMemoryHistoryTool(ctx);
|
||||
|
||||
const result = await tool.execute("call-2", { memoryId: "mem-empty" });
|
||||
|
||||
expect(result.content[0].text).toBe(
|
||||
"No history found for memory mem-empty.",
|
||||
);
|
||||
expect(result.details.count).toBe(0);
|
||||
});
|
||||
|
||||
it("handles null history", async () => {
|
||||
const ctx = createMockToolDeps({
|
||||
provider: {
|
||||
search: vi.fn(),
|
||||
add: vi.fn(),
|
||||
getAll: vi.fn(),
|
||||
update: vi.fn(),
|
||||
delete: vi.fn(),
|
||||
get: vi.fn(),
|
||||
history: vi.fn().mockResolvedValue(null),
|
||||
},
|
||||
});
|
||||
const tool = createMemoryHistoryTool(ctx);
|
||||
|
||||
const result = await tool.execute("call-3", { memoryId: "mem-null" });
|
||||
|
||||
expect(result.content[0].text).toBe(
|
||||
"No history found for memory mem-null.",
|
||||
);
|
||||
expect(result.details.count).toBe(0);
|
||||
});
|
||||
|
||||
it("handles errors gracefully", async () => {
|
||||
const ctx = createMockToolDeps({
|
||||
provider: {
|
||||
search: vi.fn(),
|
||||
add: vi.fn(),
|
||||
getAll: vi.fn(),
|
||||
update: vi.fn(),
|
||||
delete: vi.fn(),
|
||||
get: vi.fn(),
|
||||
history: vi.fn().mockRejectedValue(new Error("history unavailable")),
|
||||
},
|
||||
});
|
||||
const tool = createMemoryHistoryTool(ctx);
|
||||
|
||||
const result = await tool.execute("call-4", { memoryId: "mem-err" });
|
||||
|
||||
expect(result.content[0].text).toContain("Memory history failed");
|
||||
expect(result.content[0].text).toContain("history unavailable");
|
||||
expect(result.details.error).toContain("history unavailable");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user