chore: fixing the ci integration test and token management for integration

This commit is contained in:
kartik-mem0
2026-03-19 11:00:22 +05:30
parent 2131da5101
commit a14bc1ff24
2 changed files with 70 additions and 20 deletions
+56 -12
View File
@@ -3,9 +3,13 @@
* *
* Provides environment gating, client factory, polling helpers, * Provides environment gating, client factory, polling helpers,
* and console suppression for telemetry noise. * and console suppression for telemetry noise.
*
* API credit budget: these helpers are designed to minimize API calls.
* Each CI run should use ~40 calls total across all test files.
*/ */
import { MemoryClient } from "../../mem0"; import { MemoryClient } from "../../mem0";
import type { Memory } from "../../mem0.types"; import type { Memory } from "../../mem0.types";
import { NetworkError, RateLimitError } from "../../../common/exceptions";
// ─── Environment gate ──────────────────────────────────── // ─── Environment gate ────────────────────────────────────
export const API_KEY = process.env.MEM0_API_KEY; export const API_KEY = process.env.MEM0_API_KEY;
@@ -15,9 +19,35 @@ export const describeIntegration = API_KEY ? describe : describe.skip;
* Create a MemoryClient with the real API key. * Create a MemoryClient with the real API key.
* Call this inside beforeAll — not at module scope — so it only * Call this inside beforeAll — not at module scope — so it only
* runs when the suite is not skipped. * runs when the suite is not skipped.
*
* The returned client retries transient errors (502, 503, 504, 429)
* with backoff so CI runs are not flaky.
*/ */
export function createTestClient(): MemoryClient { export function createTestClient(): MemoryClient {
return new MemoryClient({ apiKey: API_KEY! }); const client = new MemoryClient({ apiKey: API_KEY! });
const originalFetch = (client as any)._fetchWithErrorHandling.bind(client);
(client as any)._fetchWithErrorHandling = async (
url: string,
options: any,
) => {
const MAX_RETRIES = 2;
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
try {
return await originalFetch(url, options);
} catch (error: any) {
const isTransient =
error instanceof NetworkError || error instanceof RateLimitError;
if (isTransient && attempt < MAX_RETRIES) {
await new Promise((r) => setTimeout(r, 3_000 * attempt));
continue;
}
throw error;
}
}
};
return client;
} }
/** /**
@@ -42,6 +72,27 @@ export async function waitForMemories(
return await client.getAll({ user_id: userId }); return await client.getAll({ user_id: userId });
} }
/**
* Poll search until results appear. Only used by search tests —
* other test files should NOT call this to avoid wasting API credits.
*/
export async function waitForSearchResults(
client: MemoryClient,
query: string,
options: Record<string, any>,
maxWaitMs = 30_000,
): Promise<Memory[]> {
const start = Date.now();
while (Date.now() - start < maxWaitMs) {
const results = await client.search(query, options);
if (Array.isArray(results) && results.length > 0) {
return results;
}
await new Promise((r) => setTimeout(r, 3_000));
}
return await client.search(query, options);
}
/** /**
* Suppress telemetry console noise during tests. * Suppress telemetry console noise during tests.
* Returns a cleanup function to call in afterAll. * Returns a cleanup function to call in afterAll.
@@ -69,7 +120,10 @@ export function suppressTelemetryNoise(): () => void {
/** /**
* Add test memories and wait for them to be processed. * Add test memories and wait for them to be processed.
* Returns the memory IDs once available. * Returns the memory IDs once available via getAll.
*
* NOTE: This only waits for the listing index. If your test needs
* search results, call waitForSearchResults() separately.
*/ */
export async function seedTestMemories( export async function seedTestMemories(
client: MemoryClient, client: MemoryClient,
@@ -104,17 +158,7 @@ export async function seedTestMemories(
{ user_id: userId }, { user_id: userId },
); );
// Wait for async processing (listing index)
const memories = await waitForMemories(client, userId, 1); const memories = await waitForMemories(client, userId, 1);
// Also wait for search index — it can lag behind getAll under load (e.g. CI)
const start = Date.now();
while (Date.now() - start < 60_000) {
const results = await client.search("favorite color", { user_id: userId });
if (Array.isArray(results) && results.length > 0) break;
await new Promise((r) => setTimeout(r, 3_000));
}
return memories.map((m) => m.id); return memories.map((m) => m.id);
} }
@@ -13,6 +13,7 @@ import {
suppressTelemetryNoise, suppressTelemetryNoise,
seedTestMemories, seedTestMemories,
cleanupTestUser, cleanupTestUser,
waitForSearchResults,
} from "./helpers"; } from "./helpers";
jest.setTimeout(120_000); jest.setTimeout(120_000);
@@ -38,9 +39,12 @@ describeIntegration("MemoryClient Integration — Search & History", () => {
// ─── Search v1 ──────────────────────────────────────────── // ─── Search v1 ────────────────────────────────────────────
describe("search v1", () => { describe("search v1", () => {
test("searches memories by user_id and returns results with scores", async () => { test("searches memories by user_id and returns results with scores", async () => {
const results = await client.search("What is my favorite color?", { // Search index may lag behind listing index — poll until ready
user_id: TEST_USER_ID, const results = await waitForSearchResults(
}); client,
"What is my favorite color?",
{ user_id: TEST_USER_ID },
);
expect(Array.isArray(results)).toBe(true); expect(Array.isArray(results)).toBe(true);
expect(results.length).toBeGreaterThan(0); expect(results.length).toBeGreaterThan(0);
@@ -56,12 +60,14 @@ describeIntegration("MemoryClient Integration — Search & History", () => {
// ─── Search v2 ──────────────────────────────────────────── // ─── Search v2 ────────────────────────────────────────────
describe("search v2", () => { describe("search v2", () => {
test("searches with OR filters and returns results", async () => { test("searches with OR filters and returns results", async () => {
const results = await client.search("What do you know about me?", { const results = await waitForSearchResults(
filters: { client,
OR: [{ user_id: TEST_USER_ID }], "What do you know about me?",
{
filters: { OR: [{ user_id: TEST_USER_ID }] },
api_version: "v2",
}, },
api_version: "v2", );
});
expect(Array.isArray(results)).toBe(true); expect(Array.isArray(results)).toBe(true);
expect(results.length).toBeGreaterThan(0); expect(results.length).toBeGreaterThan(0);