Files
mem0/mem0-ts/src/oss/tests/redis.unit.test.ts
T

213 lines
6.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { RedisDB } from "../src/vector_stores/redis";
jest.mock("redis", () => ({
createClient: jest.fn(() => ({
connect: jest.fn(),
on: jest.fn(),
moduleList: jest.fn().mockResolvedValue([{ name: "search", ver: 20800 }]),
ft: {
create: jest.fn(),
search: jest.fn(),
info: jest.fn().mockRejectedValue(new Error("Unknown index")),
_list: jest.fn().mockResolvedValue([]),
},
hSet: jest.fn(),
hGetAll: jest.fn(),
del: jest.fn(),
exists: jest.fn(),
quit: jest.fn(),
})),
}));
function createStore(): RedisDB {
return new RedisDB({
redisUrl: "redis://localhost:6379",
collectionName: "test",
embeddingModelDims: 4,
});
}
describe("RedisDB – entity payload handling", () => {
let store: RedisDB;
let mockClient: any;
beforeAll(async () => {
store = createStore();
await store.initialize();
mockClient = (store as any).client;
});
beforeEach(() => {
jest.clearAllMocks();
});
test("insert with entity payload (no hash/created_at) does not produce NaN", async () => {
const entityPayload = {
data: "OpenAI",
entityType: "organization",
linkedMemoryIds: ["mem-1"],
userId: "test_user",
};
await store.insert([[0.1, 0.2, 0.3, 0.4]], ["entity-1"], [entityPayload]);
expect(mockClient.hSet).toHaveBeenCalledTimes(1);
const call = mockClient.hSet.mock.calls[0];
const entry = call[1];
expect(entry.memory_id).toBe("entity-1");
expect(entry.memory).toBe("OpenAI");
expect(entry.hash).toBe("");
expect(entry.created_at).toBe(0);
expect(Number.isNaN(entry.created_at)).toBe(false);
});
test("update with entity payload (no hash/created_at/updated_at) does not produce NaN", async () => {
const entityPayload = {
data: "OpenAI",
entityType: "organization",
linkedMemoryIds: ["mem-1"],
userId: "test_user",
};
await store.update("entity-1", [0.1, 0.2, 0.3, 0.4], entityPayload);
expect(mockClient.hSet).toHaveBeenCalledTimes(1);
const call = mockClient.hSet.mock.calls[0];
const entry = call[1];
expect(entry.memory_id).toBe("entity-1");
expect(entry.memory).toBe("OpenAI");
expect(entry.hash).toBe("");
expect(entry.created_at).toBe(0);
expect(entry.updated_at).toBe(0);
expect(Number.isNaN(entry.created_at)).toBe(false);
expect(Number.isNaN(entry.updated_at)).toBe(false);
});
test("update with normal payload preserves timestamps", async () => {
const normalPayload = {
data: "likes coffee",
hash: "abc123",
createdAt: "2026-06-25T10:00:00.000Z",
updatedAt: "2026-06-25T12:00:00.000Z",
userId: "test_user",
};
await store.update("mem-1", [0.1, 0.2, 0.3, 0.4], normalPayload);
const call = mockClient.hSet.mock.calls[0];
const entry = call[1];
expect(entry.hash).toBe("abc123");
expect(entry.memory).toBe("likes coffee");
expect(entry.created_at).toBeGreaterThan(0);
expect(entry.updated_at).toBeGreaterThan(0);
expect(Number.isNaN(entry.created_at)).toBe(false);
expect(Number.isNaN(entry.updated_at)).toBe(false);
});
test("insert with normal payload preserves timestamp", async () => {
const normalPayload = {
data: "likes coffee",
hash: "abc123",
createdAt: "2026-06-25T10:00:00.000Z",
userId: "test_user",
};
await store.insert([[0.1, 0.2, 0.3, 0.4]], ["mem-1"], [normalPayload]);
const call = mockClient.hSet.mock.calls[0];
const entry = call[1];
expect(entry.hash).toBe("abc123");
expect(entry.memory).toBe("likes coffee");
expect(entry.created_at).toBeGreaterThan(0);
expect(Number.isNaN(entry.created_at)).toBe(false);
});
test("search returns entity ids as snake_case", async () => {
mockClient.ft.search.mockResolvedValue({
total: 1,
documents: [
{
id: "mem0:test:mem-1",
value: {
memory_id: "mem-1",
hash: "h1",
memory: "likes coffee",
created_at: "1700000000000",
agent_id: "agent-1",
run_id: "run-1",
user_id: "user-1",
metadata: "{}",
__vector_score: 0.1,
},
},
],
});
const results = await store.search([0.1, 0.2, 0.3, 0.4], 5);
expect(results[0].payload.user_id).toBe("user-1");
expect(results[0].payload.agent_id).toBe("agent-1");
expect(results[0].payload.run_id).toBe("run-1");
expect(results[0].payload).not.toHaveProperty("userId");
expect(results[0].payload).not.toHaveProperty("agentId");
expect(results[0].payload).not.toHaveProperty("runId");
});
test("get returns entity ids as snake_case", async () => {
mockClient.exists.mockResolvedValue(1);
mockClient.hGetAll.mockResolvedValue({
memory_id: "mem-1",
hash: "h1",
memory: "likes coffee",
created_at: "1700000000000",
agent_id: "agent-1",
run_id: "run-1",
user_id: "user-1",
metadata: "{}",
});
const result = await store.get("mem-1");
expect(result?.payload.user_id).toBe("user-1");
expect(result?.payload.agent_id).toBe("agent-1");
expect(result?.payload.run_id).toBe("run-1");
expect(result?.payload).not.toHaveProperty("userId");
expect(result?.payload).not.toHaveProperty("agentId");
expect(result?.payload).not.toHaveProperty("runId");
});
test("list returns entity ids as snake_case", async () => {
mockClient.ft.search.mockResolvedValue({
total: 1,
documents: [
{
id: "mem0:test:mem-1",
value: {
memory_id: "mem-1",
hash: "h1",
memory: "likes coffee",
created_at: "1700000000000",
agent_id: "agent-1",
run_id: "run-1",
user_id: "user-1",
metadata: "{}",
},
},
],
});
const [items] = await store.list();
expect(items[0].payload.user_id).toBe("user-1");
expect(items[0].payload.agent_id).toBe("agent-1");
expect(items[0].payload.run_id).toBe("run-1");
expect(items[0].payload).not.toHaveProperty("userId");
expect(items[0].payload).not.toHaveProperty("agentId");
expect(items[0].payload).not.toHaveProperty("runId");
});
});