chore: fixing the ci integration test and token management for integration
This commit is contained in:
@@ -3,9 +3,13 @@
|
||||
*
|
||||
* Provides environment gating, client factory, polling helpers,
|
||||
* 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 type { Memory } from "../../mem0.types";
|
||||
import { NetworkError, RateLimitError } from "../../../common/exceptions";
|
||||
|
||||
// ─── Environment gate ────────────────────────────────────
|
||||
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.
|
||||
* Call this inside beforeAll — not at module scope — so it only
|
||||
* 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 {
|
||||
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 });
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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.
|
||||
* 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(
|
||||
client: MemoryClient,
|
||||
@@ -104,17 +158,7 @@ export async function seedTestMemories(
|
||||
{ user_id: userId },
|
||||
);
|
||||
|
||||
// Wait for async processing (listing index)
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
suppressTelemetryNoise,
|
||||
seedTestMemories,
|
||||
cleanupTestUser,
|
||||
waitForSearchResults,
|
||||
} from "./helpers";
|
||||
|
||||
jest.setTimeout(120_000);
|
||||
@@ -38,9 +39,12 @@ describeIntegration("MemoryClient Integration — Search & History", () => {
|
||||
// ─── Search v1 ────────────────────────────────────────────
|
||||
describe("search v1", () => {
|
||||
test("searches memories by user_id and returns results with scores", async () => {
|
||||
const results = await client.search("What is my favorite color?", {
|
||||
user_id: TEST_USER_ID,
|
||||
});
|
||||
// Search index may lag behind listing index — poll until ready
|
||||
const results = await waitForSearchResults(
|
||||
client,
|
||||
"What is my favorite color?",
|
||||
{ user_id: TEST_USER_ID },
|
||||
);
|
||||
|
||||
expect(Array.isArray(results)).toBe(true);
|
||||
expect(results.length).toBeGreaterThan(0);
|
||||
@@ -56,12 +60,14 @@ describeIntegration("MemoryClient Integration — Search & History", () => {
|
||||
// ─── Search v2 ────────────────────────────────────────────
|
||||
describe("search v2", () => {
|
||||
test("searches with OR filters and returns results", async () => {
|
||||
const results = await client.search("What do you know about me?", {
|
||||
filters: {
|
||||
OR: [{ user_id: TEST_USER_ID }],
|
||||
},
|
||||
const results = await waitForSearchResults(
|
||||
client,
|
||||
"What do you know about me?",
|
||||
{
|
||||
filters: { OR: [{ user_id: TEST_USER_ID }] },
|
||||
api_version: "v2",
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
expect(Array.isArray(results)).toBe(true);
|
||||
expect(results.length).toBeGreaterThan(0);
|
||||
|
||||
Reference in New Issue
Block a user