feat(test): integration test for ts-sdk (#4395)
This commit is contained in:
@@ -0,0 +1,251 @@
|
||||
/**
|
||||
* Integration tests: Memory CRUD operations.
|
||||
*
|
||||
* Tests the full lifecycle: add → get → getAll → update → delete.
|
||||
* Validates response shapes against the real API.
|
||||
*
|
||||
* Run: MEM0_API_KEY=your-key npx jest crud.test.ts --forceExit
|
||||
*/
|
||||
import { MemoryClient } from "../../mem0";
|
||||
import { MemoryError } from "../../../common/exceptions";
|
||||
import { randomUUID } from "crypto";
|
||||
import {
|
||||
describeIntegration,
|
||||
createTestClient,
|
||||
suppressTelemetryNoise,
|
||||
waitForMemories,
|
||||
cleanupTestUser,
|
||||
} from "./helpers";
|
||||
|
||||
jest.setTimeout(120_000);
|
||||
|
||||
const TEST_USER_ID = `integration-crud-${randomUUID()}`;
|
||||
|
||||
describeIntegration("MemoryClient Integration — CRUD", () => {
|
||||
let client: MemoryClient;
|
||||
let cleanup: () => void;
|
||||
let memoryIds: string[] = [];
|
||||
|
||||
beforeAll(() => {
|
||||
cleanup = suppressTelemetryNoise();
|
||||
client = createTestClient();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanupTestUser(client, TEST_USER_ID);
|
||||
cleanup();
|
||||
});
|
||||
|
||||
// ─── Add ──────────────────────────────────────────────────
|
||||
describe("add memories", () => {
|
||||
test("add returns a pending response with event_id", async () => {
|
||||
const messages = [
|
||||
{
|
||||
role: "user" as const,
|
||||
content: "Hi, I'm integration-test-user. My favorite color is blue.",
|
||||
},
|
||||
{
|
||||
role: "assistant" as const,
|
||||
content:
|
||||
"Nice to meet you! I'll remember that your favorite color is blue.",
|
||||
},
|
||||
];
|
||||
|
||||
const result = await client.add(messages, { user_id: TEST_USER_ID });
|
||||
|
||||
// API processes memories asynchronously — returns PENDING
|
||||
expect(Array.isArray(result)).toBe(true);
|
||||
expect(result.length).toBeGreaterThan(0);
|
||||
|
||||
// Validate response shape
|
||||
for (const item of result) {
|
||||
expect(item).toHaveProperty("status");
|
||||
expect(item).toHaveProperty("event_id");
|
||||
}
|
||||
});
|
||||
|
||||
test("adds a second batch of messages", async () => {
|
||||
const messages = [
|
||||
{
|
||||
role: "user" as const,
|
||||
content: "I work as a software engineer at Acme Corp.",
|
||||
},
|
||||
{
|
||||
role: "assistant" as const,
|
||||
content: "Got it, you're a software engineer at Acme Corp!",
|
||||
},
|
||||
];
|
||||
|
||||
const result = await client.add(messages, { user_id: TEST_USER_ID });
|
||||
expect(Array.isArray(result)).toBe(true);
|
||||
});
|
||||
|
||||
test("memories become available after async processing", async () => {
|
||||
const memories = await waitForMemories(client, TEST_USER_ID, 1);
|
||||
|
||||
expect(memories.length).toBeGreaterThan(0);
|
||||
|
||||
// Store IDs for later tests
|
||||
memoryIds = memories.map((m) => m.id);
|
||||
expect(memoryIds.length).toBeGreaterThan(0);
|
||||
expect(typeof memoryIds[0]).toBe("string");
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Get by ID ────────────────────────────────────────────
|
||||
describe("get memory by ID", () => {
|
||||
test("retrieves a specific memory with correct shape", async () => {
|
||||
const memoryId = memoryIds[0];
|
||||
expect(memoryId).toBeDefined();
|
||||
|
||||
const memory = await client.get(memoryId);
|
||||
|
||||
expect(memory.id).toBe(memoryId);
|
||||
expect(typeof memory.memory).toBe("string");
|
||||
expect(memory.memory!.length).toBeGreaterThan(0);
|
||||
expect(typeof memory.user_id).toBe("string");
|
||||
expect(
|
||||
memory.metadata === null || typeof memory.metadata === "object",
|
||||
).toBe(true);
|
||||
expect(
|
||||
Array.isArray(memory.categories) || memory.categories === null,
|
||||
).toBe(true);
|
||||
expect(new Date(memory.created_at || "").toString()).not.toBe(
|
||||
"Invalid Date",
|
||||
);
|
||||
expect(new Date(memory.updated_at || "").toString()).not.toBe(
|
||||
"Invalid Date",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Get all ──────────────────────────────────────────────
|
||||
describe("get all memories", () => {
|
||||
test("returns all memories for test user", async () => {
|
||||
const memories = await client.getAll({ user_id: TEST_USER_ID });
|
||||
|
||||
expect(Array.isArray(memories)).toBe(true);
|
||||
expect(memories.length).toBeGreaterThanOrEqual(memoryIds.length);
|
||||
|
||||
for (const mem of memories) {
|
||||
expect(typeof mem.id).toBe("string");
|
||||
expect(typeof mem.memory).toBe("string");
|
||||
}
|
||||
});
|
||||
|
||||
test("returns paginated results with page and page_size", async () => {
|
||||
const page1 = await client.getAll({
|
||||
user_id: TEST_USER_ID,
|
||||
page: 1,
|
||||
page_size: 1,
|
||||
});
|
||||
|
||||
// Paginated response is an object with results array
|
||||
expect(page1).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Update ───────────────────────────────────────────────
|
||||
describe("update memory", () => {
|
||||
test("updates memory text and verifies the content changed", async () => {
|
||||
const memoryId = memoryIds[0];
|
||||
|
||||
// Read original text before update
|
||||
const original = await client.get(memoryId);
|
||||
const originalText = original.memory;
|
||||
|
||||
await client.update(memoryId, {
|
||||
text: "My favorite color is green (updated)",
|
||||
});
|
||||
|
||||
const updated = await client.get(memoryId);
|
||||
expect(typeof updated.memory).toBe("string");
|
||||
expect(updated.memory).not.toBe(originalText);
|
||||
});
|
||||
|
||||
test("updates memory metadata", async () => {
|
||||
const memoryId = memoryIds[0];
|
||||
|
||||
await client.update(memoryId, {
|
||||
metadata: { source: "integration-test", priority: "high" },
|
||||
});
|
||||
|
||||
const updated = await client.get(memoryId);
|
||||
expect(updated.metadata).toBeDefined();
|
||||
expect(updated.metadata.source).toBe("integration-test");
|
||||
expect(updated.metadata.priority).toBe("high");
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Delete single ────────────────────────────────────────
|
||||
describe("delete memory", () => {
|
||||
test("deletes a single memory by ID", async () => {
|
||||
const memoryId = memoryIds[0];
|
||||
expect(memoryId).toBeDefined();
|
||||
|
||||
const result = await client.delete(memoryId);
|
||||
expect(result).toBeDefined();
|
||||
expect(typeof result.message).toBe("string");
|
||||
});
|
||||
|
||||
test("getting deleted memory throws MemoryError", async () => {
|
||||
const memoryId = memoryIds[0];
|
||||
await expect(client.get(memoryId)).rejects.toThrow(MemoryError);
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Edge cases ──────────────────────────────────────────
|
||||
describe("edge cases", () => {
|
||||
test("add with metadata attaches metadata to the memory", async () => {
|
||||
const result = await client.add(
|
||||
[
|
||||
{ role: "user" as const, content: "I prefer dark mode in all apps." },
|
||||
{
|
||||
role: "assistant" as const,
|
||||
content: "Noted, dark mode preference saved!",
|
||||
},
|
||||
],
|
||||
{
|
||||
user_id: TEST_USER_ID,
|
||||
metadata: { source: "integration-test", category: "preferences" },
|
||||
},
|
||||
);
|
||||
|
||||
expect(Array.isArray(result)).toBe(true);
|
||||
expect(result.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test("getAll for non-existent user returns empty array", async () => {
|
||||
const memories = await client.getAll({
|
||||
user_id: `nonexistent-user-${randomUUID()}`,
|
||||
});
|
||||
|
||||
expect(Array.isArray(memories)).toBe(true);
|
||||
expect(memories.length).toBe(0);
|
||||
});
|
||||
|
||||
test("deleteAll for non-existent user does not throw", async () => {
|
||||
const result = await client.deleteAll({
|
||||
user_id: `nonexistent-user-${randomUUID()}`,
|
||||
});
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(typeof result.message).toBe("string");
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Delete all + delete user ─────────────────────────────
|
||||
describe("cleanup operations", () => {
|
||||
test("deletes all memories for test user", async () => {
|
||||
const result = await client.deleteAll({ user_id: TEST_USER_ID });
|
||||
expect(result).toBeDefined();
|
||||
expect(typeof result.message).toBe("string");
|
||||
});
|
||||
|
||||
test("deletes the test user entity", async () => {
|
||||
const result = await client.deleteUsers({ user_id: TEST_USER_ID });
|
||||
expect(result).toBeDefined();
|
||||
expect(result.message).toBe("Entity deleted successfully.");
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user