@cortexmemory/cli 0.27.3 → 0.27.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +127 -56
- package/dist/commands/deploy.js.map +1 -1
- package/dist/utils/app-template-sync.d.ts.map +1 -1
- package/dist/utils/app-template-sync.js +32 -12
- package/dist/utils/app-template-sync.js.map +1 -1
- package/package.json +1 -1
- package/templates/vercel-ai-quickstart/app/api/auth/login/route.ts +56 -11
- package/templates/vercel-ai-quickstart/app/api/chat/route.ts +70 -15
- package/templates/vercel-ai-quickstart/app/api/chat-v6/route.ts +333 -0
- package/templates/vercel-ai-quickstart/app/page.tsx +18 -4
- package/templates/vercel-ai-quickstart/components/ChatInterface.tsx +5 -2
- package/templates/vercel-ai-quickstart/jest.config.js +8 -1
- package/templates/vercel-ai-quickstart/lib/agents/memory-agent.ts +165 -0
- package/templates/vercel-ai-quickstart/lib/versions.ts +60 -0
- package/templates/vercel-ai-quickstart/package.json +5 -1
- package/templates/vercel-ai-quickstart/test-api.mjs +272 -0
- package/templates/vercel-ai-quickstart/tests/e2e/chat-memory-flow.test.ts +454 -0
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E2E Tests: Chat API Memory Flow
|
|
3
|
+
*
|
|
4
|
+
* These tests verify the FULL memory flow through the chat API routes:
|
|
5
|
+
* - Fact storage from conversations
|
|
6
|
+
* - Belief revision (superseding facts)
|
|
7
|
+
* - Memory recall across conversations
|
|
8
|
+
* - Conversation lifecycle (create, chat, delete)
|
|
9
|
+
*
|
|
10
|
+
* REQUIRES:
|
|
11
|
+
* - CONVEX_URL: Real Convex deployment
|
|
12
|
+
* - OPENAI_API_KEY: For LLM calls and embeddings
|
|
13
|
+
* - CORTEX_FACT_EXTRACTION=true: Enable fact extraction
|
|
14
|
+
*
|
|
15
|
+
* These tests use real HTTP requests to the quickstart server,
|
|
16
|
+
* so the server must be running on localhost:3000.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { Cortex } from "@cortexmemory/sdk";
|
|
20
|
+
|
|
21
|
+
// Skip if required env vars not set
|
|
22
|
+
const SKIP_E2E =
|
|
23
|
+
!process.env.CONVEX_URL ||
|
|
24
|
+
!process.env.OPENAI_API_KEY ||
|
|
25
|
+
!process.env.QUICKSTART_URL;
|
|
26
|
+
|
|
27
|
+
const BASE_URL = process.env.QUICKSTART_URL || "http://localhost:3000";
|
|
28
|
+
|
|
29
|
+
// Generate unique IDs for test isolation
|
|
30
|
+
function generateTestId(prefix: string): string {
|
|
31
|
+
const timestamp = Date.now();
|
|
32
|
+
const random = Math.random().toString(36).slice(2, 8);
|
|
33
|
+
return `${prefix}-e2e-${timestamp}-${random}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Make a chat request to the API
|
|
38
|
+
*/
|
|
39
|
+
async function sendChatMessage(
|
|
40
|
+
endpoint: string,
|
|
41
|
+
messages: Array<{ role: string; content: string }>,
|
|
42
|
+
options: {
|
|
43
|
+
userId: string;
|
|
44
|
+
memorySpaceId: string;
|
|
45
|
+
conversationId?: string;
|
|
46
|
+
}
|
|
47
|
+
): Promise<{
|
|
48
|
+
response: string;
|
|
49
|
+
conversationId?: string;
|
|
50
|
+
}> {
|
|
51
|
+
const response = await fetch(`${BASE_URL}/api/${endpoint}`, {
|
|
52
|
+
method: "POST",
|
|
53
|
+
headers: { "Content-Type": "application/json" },
|
|
54
|
+
body: JSON.stringify({
|
|
55
|
+
messages: messages.map((m, i) => ({
|
|
56
|
+
id: `msg-${i}`,
|
|
57
|
+
role: m.role,
|
|
58
|
+
content: m.content,
|
|
59
|
+
createdAt: new Date().toISOString(),
|
|
60
|
+
})),
|
|
61
|
+
userId: options.userId,
|
|
62
|
+
memorySpaceId: options.memorySpaceId,
|
|
63
|
+
conversationId: options.conversationId,
|
|
64
|
+
}),
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
if (!response.ok) {
|
|
68
|
+
const error = await response.text();
|
|
69
|
+
throw new Error(`Chat API error: ${response.status} - ${error}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Parse streaming response
|
|
73
|
+
const text = await response.text();
|
|
74
|
+
|
|
75
|
+
// Extract text content from the stream (simplified parsing)
|
|
76
|
+
let fullResponse = "";
|
|
77
|
+
let conversationId: string | undefined;
|
|
78
|
+
|
|
79
|
+
const lines = text.split("\n");
|
|
80
|
+
for (const line of lines) {
|
|
81
|
+
if (line.startsWith("0:")) {
|
|
82
|
+
// Text content
|
|
83
|
+
try {
|
|
84
|
+
const content = JSON.parse(line.slice(2));
|
|
85
|
+
if (typeof content === "string") {
|
|
86
|
+
fullResponse += content;
|
|
87
|
+
}
|
|
88
|
+
} catch {
|
|
89
|
+
// Ignore parse errors
|
|
90
|
+
}
|
|
91
|
+
} else if (line.includes("data-conversation-update")) {
|
|
92
|
+
// Extract conversation ID
|
|
93
|
+
try {
|
|
94
|
+
const match = line.match(/"conversationId":"([^"]+)"/);
|
|
95
|
+
if (match) {
|
|
96
|
+
conversationId = match[1];
|
|
97
|
+
}
|
|
98
|
+
} catch {
|
|
99
|
+
// Ignore parse errors
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return { response: fullResponse, conversationId };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
describe("Chat Memory Flow E2E", () => {
|
|
108
|
+
let cortex: Cortex;
|
|
109
|
+
let testUserId: string;
|
|
110
|
+
let testMemorySpaceId: string;
|
|
111
|
+
|
|
112
|
+
beforeAll(() => {
|
|
113
|
+
if (SKIP_E2E) {
|
|
114
|
+
console.log(
|
|
115
|
+
"Skipping E2E tests - CONVEX_URL, OPENAI_API_KEY, or QUICKSTART_URL not configured"
|
|
116
|
+
);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
cortex = new Cortex({ convexUrl: process.env.CONVEX_URL! });
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
beforeEach(() => {
|
|
123
|
+
if (SKIP_E2E) return;
|
|
124
|
+
testUserId = generateTestId("user");
|
|
125
|
+
testMemorySpaceId = generateTestId("space");
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
afterAll(async () => {
|
|
129
|
+
if (cortex) {
|
|
130
|
+
cortex.close();
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
135
|
+
// V5 Route Tests
|
|
136
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
137
|
+
|
|
138
|
+
(SKIP_E2E ? describe.skip : describe)("v5 route (/api/chat)", () => {
|
|
139
|
+
it("should store facts from conversation", async () => {
|
|
140
|
+
// Send a message with a fact
|
|
141
|
+
await sendChatMessage(
|
|
142
|
+
"chat",
|
|
143
|
+
[{ role: "user", content: "My name is Alice and I work as a software engineer" }],
|
|
144
|
+
{ userId: testUserId, memorySpaceId: testMemorySpaceId }
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
// Wait for fact extraction
|
|
148
|
+
await new Promise((r) => setTimeout(r, 5000));
|
|
149
|
+
|
|
150
|
+
// Verify facts were stored
|
|
151
|
+
const facts = await cortex.facts.list({
|
|
152
|
+
memorySpaceId: testMemorySpaceId,
|
|
153
|
+
userId: testUserId,
|
|
154
|
+
includeSuperseded: false,
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
console.log(`[V5] Stored facts: ${facts.length}`);
|
|
158
|
+
facts.forEach((f) => console.log(` - ${f.fact}`));
|
|
159
|
+
|
|
160
|
+
expect(facts.length).toBeGreaterThan(0);
|
|
161
|
+
}, 60000);
|
|
162
|
+
|
|
163
|
+
it("should supersede facts through belief revision", async () => {
|
|
164
|
+
// First message: establish a preference
|
|
165
|
+
await sendChatMessage(
|
|
166
|
+
"chat",
|
|
167
|
+
[{ role: "user", content: "My favorite color is blue" }],
|
|
168
|
+
{ userId: testUserId, memorySpaceId: testMemorySpaceId }
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
await new Promise((r) => setTimeout(r, 5000));
|
|
172
|
+
|
|
173
|
+
// Second message: change the preference
|
|
174
|
+
await sendChatMessage(
|
|
175
|
+
"chat",
|
|
176
|
+
[
|
|
177
|
+
{ role: "user", content: "My favorite color is blue" },
|
|
178
|
+
{ role: "assistant", content: "Got it, blue is your favorite color!" },
|
|
179
|
+
{ role: "user", content: "Actually, my favorite color is purple now" },
|
|
180
|
+
],
|
|
181
|
+
{ userId: testUserId, memorySpaceId: testMemorySpaceId }
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
await new Promise((r) => setTimeout(r, 5000));
|
|
185
|
+
|
|
186
|
+
// Check facts
|
|
187
|
+
const allFacts = await cortex.facts.list({
|
|
188
|
+
memorySpaceId: testMemorySpaceId,
|
|
189
|
+
userId: testUserId,
|
|
190
|
+
includeSuperseded: true,
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
const activeFacts = await cortex.facts.list({
|
|
194
|
+
memorySpaceId: testMemorySpaceId,
|
|
195
|
+
userId: testUserId,
|
|
196
|
+
includeSuperseded: false,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
console.log(`[V5] All facts: ${allFacts.length}, Active: ${activeFacts.length}`);
|
|
200
|
+
allFacts.forEach((f) => {
|
|
201
|
+
const status = f.supersededBy ? "SUPERSEDED" : "ACTIVE";
|
|
202
|
+
console.log(` [${status}] ${f.fact}`);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// Should have superseded the old color preference
|
|
206
|
+
const colorFacts = activeFacts.filter(
|
|
207
|
+
(f) =>
|
|
208
|
+
f.fact.toLowerCase().includes("color") ||
|
|
209
|
+
f.fact.toLowerCase().includes("purple") ||
|
|
210
|
+
f.fact.toLowerCase().includes("blue")
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
// Ideally only one active color fact (purple)
|
|
214
|
+
expect(colorFacts.length).toBeLessThanOrEqual(2);
|
|
215
|
+
}, 90000);
|
|
216
|
+
|
|
217
|
+
it("should recall facts in subsequent conversations", async () => {
|
|
218
|
+
// First conversation: store a fact
|
|
219
|
+
const conv1Result = await sendChatMessage(
|
|
220
|
+
"chat",
|
|
221
|
+
[{ role: "user", content: "I have a dog named Max" }],
|
|
222
|
+
{ userId: testUserId, memorySpaceId: testMemorySpaceId }
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
await new Promise((r) => setTimeout(r, 5000));
|
|
226
|
+
|
|
227
|
+
// Second conversation: ask about the fact
|
|
228
|
+
const conv2Result = await sendChatMessage(
|
|
229
|
+
"chat",
|
|
230
|
+
[{ role: "user", content: "What do you remember about my pets?" }],
|
|
231
|
+
{ userId: testUserId, memorySpaceId: testMemorySpaceId }
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
console.log(`[V5] Recall response: ${conv2Result.response.slice(0, 200)}...`);
|
|
235
|
+
|
|
236
|
+
// Response should mention Max (the dog)
|
|
237
|
+
const responseText = conv2Result.response.toLowerCase();
|
|
238
|
+
const mentionsPet = responseText.includes("max") || responseText.includes("dog");
|
|
239
|
+
|
|
240
|
+
// Note: LLM responses are non-deterministic, so we just verify we got a response
|
|
241
|
+
expect(conv2Result.response.length).toBeGreaterThan(0);
|
|
242
|
+
}, 90000);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
246
|
+
// V6 Route Tests
|
|
247
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
248
|
+
|
|
249
|
+
(SKIP_E2E ? describe.skip : describe)("v6 route (/api/chat-v6)", () => {
|
|
250
|
+
it("should store facts from conversation", async () => {
|
|
251
|
+
// Send a message with a fact
|
|
252
|
+
await sendChatMessage(
|
|
253
|
+
"chat-v6",
|
|
254
|
+
[{ role: "user", content: "My name is Bob and I'm a data scientist" }],
|
|
255
|
+
{ userId: testUserId, memorySpaceId: testMemorySpaceId }
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
// Wait for fact extraction
|
|
259
|
+
await new Promise((r) => setTimeout(r, 5000));
|
|
260
|
+
|
|
261
|
+
// Verify facts were stored
|
|
262
|
+
const facts = await cortex.facts.list({
|
|
263
|
+
memorySpaceId: testMemorySpaceId,
|
|
264
|
+
userId: testUserId,
|
|
265
|
+
includeSuperseded: false,
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
console.log(`[V6] Stored facts: ${facts.length}`);
|
|
269
|
+
facts.forEach((f) => console.log(` - ${f.fact}`));
|
|
270
|
+
|
|
271
|
+
expect(facts.length).toBeGreaterThan(0);
|
|
272
|
+
}, 60000);
|
|
273
|
+
|
|
274
|
+
it("should supersede facts through belief revision", async () => {
|
|
275
|
+
// First message: establish a preference
|
|
276
|
+
await sendChatMessage(
|
|
277
|
+
"chat-v6",
|
|
278
|
+
[{ role: "user", content: "I prefer tea over coffee" }],
|
|
279
|
+
{ userId: testUserId, memorySpaceId: testMemorySpaceId }
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
await new Promise((r) => setTimeout(r, 5000));
|
|
283
|
+
|
|
284
|
+
// Second message: change the preference
|
|
285
|
+
await sendChatMessage(
|
|
286
|
+
"chat-v6",
|
|
287
|
+
[
|
|
288
|
+
{ role: "user", content: "I prefer tea over coffee" },
|
|
289
|
+
{ role: "assistant", content: "Got it, you prefer tea!" },
|
|
290
|
+
{ role: "user", content: "Actually I've switched to coffee now, it helps me focus" },
|
|
291
|
+
],
|
|
292
|
+
{ userId: testUserId, memorySpaceId: testMemorySpaceId }
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
await new Promise((r) => setTimeout(r, 5000));
|
|
296
|
+
|
|
297
|
+
// Check facts
|
|
298
|
+
const allFacts = await cortex.facts.list({
|
|
299
|
+
memorySpaceId: testMemorySpaceId,
|
|
300
|
+
userId: testUserId,
|
|
301
|
+
includeSuperseded: true,
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
const activeFacts = await cortex.facts.list({
|
|
305
|
+
memorySpaceId: testMemorySpaceId,
|
|
306
|
+
userId: testUserId,
|
|
307
|
+
includeSuperseded: false,
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
console.log(`[V6] All facts: ${allFacts.length}, Active: ${activeFacts.length}`);
|
|
311
|
+
allFacts.forEach((f) => {
|
|
312
|
+
const status = f.supersededBy ? "SUPERSEDED" : "ACTIVE";
|
|
313
|
+
console.log(` [${status}] ${f.fact}`);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
// Should have at least one fact about beverages
|
|
317
|
+
const beverageFacts = allFacts.filter(
|
|
318
|
+
(f) =>
|
|
319
|
+
f.fact.toLowerCase().includes("tea") ||
|
|
320
|
+
f.fact.toLowerCase().includes("coffee")
|
|
321
|
+
);
|
|
322
|
+
expect(beverageFacts.length).toBeGreaterThan(0);
|
|
323
|
+
}, 90000);
|
|
324
|
+
|
|
325
|
+
it("should recall facts in subsequent conversations", async () => {
|
|
326
|
+
// First conversation: store a fact
|
|
327
|
+
await sendChatMessage(
|
|
328
|
+
"chat-v6",
|
|
329
|
+
[{ role: "user", content: "I live in San Francisco" }],
|
|
330
|
+
{ userId: testUserId, memorySpaceId: testMemorySpaceId }
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
await new Promise((r) => setTimeout(r, 5000));
|
|
334
|
+
|
|
335
|
+
// Second conversation: ask about the fact
|
|
336
|
+
const conv2Result = await sendChatMessage(
|
|
337
|
+
"chat-v6",
|
|
338
|
+
[{ role: "user", content: "Where do I live?" }],
|
|
339
|
+
{ userId: testUserId, memorySpaceId: testMemorySpaceId }
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
console.log(`[V6] Recall response: ${conv2Result.response.slice(0, 200)}...`);
|
|
343
|
+
|
|
344
|
+
// Verify we got a response
|
|
345
|
+
expect(conv2Result.response.length).toBeGreaterThan(0);
|
|
346
|
+
}, 90000);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
350
|
+
// Feature Parity Tests
|
|
351
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
352
|
+
|
|
353
|
+
(SKIP_E2E ? describe.skip : describe)("v5 vs v6 feature parity", () => {
|
|
354
|
+
it("both routes should store facts for the same message", async () => {
|
|
355
|
+
const v5UserId = generateTestId("user-v5");
|
|
356
|
+
const v6UserId = generateTestId("user-v6");
|
|
357
|
+
const sharedSpaceId = testMemorySpaceId;
|
|
358
|
+
|
|
359
|
+
// Send same message to both routes
|
|
360
|
+
const message = "I am a TypeScript developer with 5 years of experience";
|
|
361
|
+
|
|
362
|
+
await Promise.all([
|
|
363
|
+
sendChatMessage(
|
|
364
|
+
"chat",
|
|
365
|
+
[{ role: "user", content: message }],
|
|
366
|
+
{ userId: v5UserId, memorySpaceId: sharedSpaceId }
|
|
367
|
+
),
|
|
368
|
+
sendChatMessage(
|
|
369
|
+
"chat-v6",
|
|
370
|
+
[{ role: "user", content: message }],
|
|
371
|
+
{ userId: v6UserId, memorySpaceId: sharedSpaceId }
|
|
372
|
+
),
|
|
373
|
+
]);
|
|
374
|
+
|
|
375
|
+
// Wait for fact extraction
|
|
376
|
+
await new Promise((r) => setTimeout(r, 7000));
|
|
377
|
+
|
|
378
|
+
// Check facts for both users
|
|
379
|
+
const [v5Facts, v6Facts] = await Promise.all([
|
|
380
|
+
cortex.facts.list({
|
|
381
|
+
memorySpaceId: sharedSpaceId,
|
|
382
|
+
userId: v5UserId,
|
|
383
|
+
includeSuperseded: false,
|
|
384
|
+
}),
|
|
385
|
+
cortex.facts.list({
|
|
386
|
+
memorySpaceId: sharedSpaceId,
|
|
387
|
+
userId: v6UserId,
|
|
388
|
+
includeSuperseded: false,
|
|
389
|
+
}),
|
|
390
|
+
]);
|
|
391
|
+
|
|
392
|
+
console.log(`V5 facts: ${v5Facts.length}, V6 facts: ${v6Facts.length}`);
|
|
393
|
+
console.log("V5 facts:", v5Facts.map((f) => f.fact));
|
|
394
|
+
console.log("V6 facts:", v6Facts.map((f) => f.fact));
|
|
395
|
+
|
|
396
|
+
// CRITICAL: Both routes should store facts
|
|
397
|
+
expect(v5Facts.length).toBeGreaterThan(0);
|
|
398
|
+
expect(v6Facts.length).toBeGreaterThan(0);
|
|
399
|
+
|
|
400
|
+
// Fact counts should be similar (allow some variance due to LLM non-determinism)
|
|
401
|
+
const diff = Math.abs(v5Facts.length - v6Facts.length);
|
|
402
|
+
expect(diff).toBeLessThanOrEqual(2);
|
|
403
|
+
}, 90000);
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
407
|
+
// Conversation Lifecycle
|
|
408
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
409
|
+
|
|
410
|
+
(SKIP_E2E ? describe.skip : describe)("conversation lifecycle", () => {
|
|
411
|
+
it("should create, list, and delete conversations", async () => {
|
|
412
|
+
// Create a conversation via chat
|
|
413
|
+
const chatResult = await sendChatMessage(
|
|
414
|
+
"chat",
|
|
415
|
+
[{ role: "user", content: "Hello, this is a test conversation" }],
|
|
416
|
+
{ userId: testUserId, memorySpaceId: testMemorySpaceId }
|
|
417
|
+
);
|
|
418
|
+
|
|
419
|
+
// Wait for conversation to be created
|
|
420
|
+
await new Promise((r) => setTimeout(r, 2000));
|
|
421
|
+
|
|
422
|
+
// List conversations
|
|
423
|
+
const listResponse = await fetch(
|
|
424
|
+
`${BASE_URL}/api/conversations?userId=${testUserId}&memorySpaceId=${testMemorySpaceId}`
|
|
425
|
+
);
|
|
426
|
+
const listData = await listResponse.json();
|
|
427
|
+
|
|
428
|
+
console.log(`Conversations: ${JSON.stringify(listData.conversations, null, 2)}`);
|
|
429
|
+
expect(listData.conversations).toBeDefined();
|
|
430
|
+
expect(listData.conversations.length).toBeGreaterThan(0);
|
|
431
|
+
|
|
432
|
+
// Delete conversation
|
|
433
|
+
const convId = listData.conversations[0].id;
|
|
434
|
+
const deleteResponse = await fetch(
|
|
435
|
+
`${BASE_URL}/api/conversations?conversationId=${convId}`,
|
|
436
|
+
{ method: "DELETE" }
|
|
437
|
+
);
|
|
438
|
+
const deleteData = await deleteResponse.json();
|
|
439
|
+
|
|
440
|
+
expect(deleteData.success).toBe(true);
|
|
441
|
+
|
|
442
|
+
// Verify deletion
|
|
443
|
+
const listAfterDelete = await fetch(
|
|
444
|
+
`${BASE_URL}/api/conversations?userId=${testUserId}&memorySpaceId=${testMemorySpaceId}`
|
|
445
|
+
);
|
|
446
|
+
const listAfterDeleteData = await listAfterDelete.json();
|
|
447
|
+
|
|
448
|
+
// Should have one less conversation
|
|
449
|
+
expect(listAfterDeleteData.conversations.length).toBeLessThan(
|
|
450
|
+
listData.conversations.length
|
|
451
|
+
);
|
|
452
|
+
}, 60000);
|
|
453
|
+
});
|
|
454
|
+
});
|