fix(ts/redis): use nullish coalescing for hash/timestamps in insert/update (#5860)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Hrushikesh Yadav
2026-06-26 19:01:58 +05:30
committed by GitHub
parent 08da741a31
commit f4ccef5157
2 changed files with 144 additions and 7 deletions
+16 -7
View File
@@ -337,11 +337,14 @@ export class RedisDB implements VectorStore {
const id = ids[idx];
// Create entry with required fields
const createdAt = payload.created_at
? new Date(payload.created_at).getTime()
: 0;
const entry: Record<string, any> = {
memory_id: id,
hash: payload.hash,
memory: payload.data,
created_at: new Date(payload.created_at).getTime(),
hash: payload.hash ?? "",
memory: payload.data ?? "",
created_at: createdAt,
embedding: new Float32Array(vector).buffer,
};
@@ -561,12 +564,18 @@ export class RedisDB implements VectorStore {
payload: Record<string, any>,
): Promise<void> {
const snakePayload = toSnakeCase(payload);
const createdAt = snakePayload.created_at
? new Date(snakePayload.created_at).getTime()
: 0;
const updatedAt = snakePayload.updated_at
? new Date(snakePayload.updated_at).getTime()
: 0;
const entry: Record<string, any> = {
memory_id: vectorId,
hash: snakePayload.hash,
memory: snakePayload.data,
created_at: new Date(snakePayload.created_at).getTime(),
updated_at: new Date(snakePayload.updated_at).getTime(),
hash: snakePayload.hash ?? "",
memory: snakePayload.data ?? "",
created_at: createdAt,
updated_at: updatedAt,
embedding: Buffer.from(new Float32Array(vector).buffer),
};
+128
View File
@@ -0,0 +1,128 @@
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);
});
});