test: add unit tests for CLI commands, telemetry, and fs-safe utilities
This commit is contained in:
@@ -153,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",
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1232,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"),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user