fix(ts-tests): stop integration runs from deleting each other's data
The integration suite claimed exclusive ownership of a shared project. Its
globalSetup and globalTeardown each ran a project-wide wipe:
deleteAll({userId: '*', agentId: '*', appId: '*', runId: '*'})
deleteUsers()
CI runs this suite concurrently across PRs against one MEM0_API_KEY, so each
job's wipe deleted the memories its neighbours had just seeded. maxWorkers: 1
and max-parallel: 1 only serialize within a run, not across them.
Evidence from three jobs that overlapped on 2026-08-12:
6903 crud seeded 13:53:24-13:54:15, hit by 6928's wipe (13:53:20-30) FAIL
6928 crud seeded 13:53:30-13:54:19, hit by 6902's wipe (13:53:31-43) FAIL
6902 crud seeded 13:53:43-13:54:21, no overlapping wipe PASS
6902 batch ran 13:55:31-13:56:20, hit by 6928's teardown (13:55:59) FAIL
Every failure surfaced as waitForMemories timing out with zero memories, which
reads as pipeline latency but was deletion: probing the live API shows a seeded
user settling in 17s, well inside the 45s budget.
- drop globalSetup/globalTeardown; every suite already cleans up its own
UUID-scoped user in afterAll
- remove the fullProjectCleanup helper so the wipe cannot come back
- give updateWebhook a per-run URL; the project enforces uniqueness on
(project, url), so concurrent runs collided on the hardcoded constant
Verified: two suites run concurrently against the same key go from 3 failures
to 1. The remainder is updateProject() mutating project-level
custom_instructions, a singleton two runs cannot both own.
This commit is contained in:
@@ -2,8 +2,6 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
...require("./jest.config"),
|
...require("./jest.config"),
|
||||||
testMatch: ["**/integration/**/*.test.ts"],
|
testMatch: ["**/integration/**/*.test.ts"],
|
||||||
globalSetup: "<rootDir>/src/client/tests/integration/global-setup.ts",
|
|
||||||
globalTeardown: "<rootDir>/src/client/tests/integration/global-teardown.ts",
|
|
||||||
// Run integration tests serially to avoid rate limiting and race conditions
|
// Run integration tests serially to avoid rate limiting and race conditions
|
||||||
maxWorkers: 1,
|
maxWorkers: 1,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -220,32 +220,3 @@ export async function cleanupTestUser(
|
|||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Full project wipe — deletes all memories and all entities.
|
|
||||||
* Equivalent to Python SDK's:
|
|
||||||
* client.delete_all(user_id="*", agent_id="*", app_id="*", run_id="*")
|
|
||||||
*
|
|
||||||
* Used as cleanup before and after integration test runs so tests
|
|
||||||
* start from a clean slate and don't leave data behind.
|
|
||||||
*/
|
|
||||||
export async function fullProjectCleanup(client: MemoryClient): Promise<void> {
|
|
||||||
// Delete all memories — all four filters set explicitly
|
|
||||||
try {
|
|
||||||
await client.deleteAll({
|
|
||||||
userId: "*",
|
|
||||||
agentId: "*",
|
|
||||||
appId: "*",
|
|
||||||
runId: "*",
|
|
||||||
});
|
|
||||||
} catch {
|
|
||||||
// ignore — may 404 if no data exists
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete all entities (users, agents, apps, runs)
|
|
||||||
try {
|
|
||||||
await client.deleteUsers();
|
|
||||||
} catch {
|
|
||||||
// ignore — may throw "No entities to delete"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ describeIntegration("MemoryClient Integration — Users & Project", () => {
|
|||||||
const hookName = `test-hook-${randomUUID().slice(0, 8)}`;
|
const hookName = `test-hook-${randomUUID().slice(0, 8)}`;
|
||||||
const hookUrl = `https://example.com/webhook/${randomUUID().slice(0, 8)}`;
|
const hookUrl = `https://example.com/webhook/${randomUUID().slice(0, 8)}`;
|
||||||
const updatedName = `updated-hook-${randomUUID().slice(0, 8)}`;
|
const updatedName = `updated-hook-${randomUUID().slice(0, 8)}`;
|
||||||
|
const updatedUrl = `https://example.com/updated/${randomUUID().slice(0, 8)}`;
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
if (createdWebhookId) {
|
if (createdWebhookId) {
|
||||||
@@ -168,7 +169,7 @@ describeIntegration("MemoryClient Integration — Users & Project", () => {
|
|||||||
client.updateWebhook({
|
client.updateWebhook({
|
||||||
webhookId: createdWebhookId,
|
webhookId: createdWebhookId,
|
||||||
name: updatedName,
|
name: updatedName,
|
||||||
url: "https://example.com/updated",
|
url: updatedUrl,
|
||||||
eventTypes: [WebhookEvent.MEMORY_DELETED],
|
eventTypes: [WebhookEvent.MEMORY_DELETED],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user