@ekodb/ekodb-client 0.13.0 → 0.14.0

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.
@@ -1085,6 +1085,100 @@ function mockErrorResponse(status, message) {
1085
1085
  });
1086
1086
  });
1087
1087
  // ============================================================================
1088
+ // rawCompletionStream (SSE) Tests
1089
+ // ============================================================================
1090
+ (0, vitest_1.describe)("EkoDBClient rawCompletionStream", () => {
1091
+ (0, vitest_1.it)("parses SSE done event and returns content", async () => {
1092
+ const client = createTestClient();
1093
+ mockTokenResponse();
1094
+ // SSE response with token chunks and a done event
1095
+ const sseBody = [
1096
+ 'data: {"token":"Hello"}',
1097
+ 'data: {"token":" world"}',
1098
+ 'data: {"content":"Hello world","done":true}',
1099
+ "",
1100
+ ].join("\n");
1101
+ mockFetch.mockResolvedValueOnce({
1102
+ ok: true,
1103
+ status: 200,
1104
+ text: async () => sseBody,
1105
+ headers: new Headers(),
1106
+ });
1107
+ const result = await client.rawCompletionStream({
1108
+ system_prompt: "System.",
1109
+ message: "User.",
1110
+ });
1111
+ (0, vitest_1.expect)(result.content).toBe("Hello world");
1112
+ });
1113
+ (0, vitest_1.it)("accumulates tokens when no done event", async () => {
1114
+ const client = createTestClient();
1115
+ mockTokenResponse();
1116
+ const sseBody = [
1117
+ 'data: {"token":"chunk1"}',
1118
+ 'data: {"token":"chunk2"}',
1119
+ "",
1120
+ ].join("\n");
1121
+ mockFetch.mockResolvedValueOnce({
1122
+ ok: true,
1123
+ status: 200,
1124
+ text: async () => sseBody,
1125
+ headers: new Headers(),
1126
+ });
1127
+ const result = await client.rawCompletionStream({
1128
+ system_prompt: "System.",
1129
+ message: "User.",
1130
+ });
1131
+ (0, vitest_1.expect)(result.content).toBe("chunk1chunk2");
1132
+ });
1133
+ (0, vitest_1.it)("throws on SSE error event", async () => {
1134
+ const client = createTestClient();
1135
+ mockTokenResponse();
1136
+ const sseBody = 'data: {"error":"LLM timeout"}\n';
1137
+ mockFetch.mockResolvedValueOnce({
1138
+ ok: true,
1139
+ status: 200,
1140
+ text: async () => sseBody,
1141
+ headers: new Headers(),
1142
+ });
1143
+ await (0, vitest_1.expect)(client.rawCompletionStream({
1144
+ system_prompt: "System.",
1145
+ message: "User.",
1146
+ })).rejects.toThrow("LLM timeout");
1147
+ });
1148
+ (0, vitest_1.it)("throws on non-200 HTTP response", async () => {
1149
+ const client = createTestClient();
1150
+ mockTokenResponse();
1151
+ mockFetch.mockResolvedValueOnce({
1152
+ ok: false,
1153
+ status: 401,
1154
+ text: async () => "Unauthorized",
1155
+ headers: new Headers(),
1156
+ });
1157
+ await (0, vitest_1.expect)(client.rawCompletionStream({
1158
+ system_prompt: "System.",
1159
+ message: "User.",
1160
+ })).rejects.toThrow("401");
1161
+ });
1162
+ (0, vitest_1.it)("calls the /stream endpoint", async () => {
1163
+ const client = createTestClient();
1164
+ mockTokenResponse();
1165
+ mockFetch.mockResolvedValueOnce({
1166
+ ok: true,
1167
+ status: 200,
1168
+ text: async () => 'data: {"content":"ok"}\n',
1169
+ headers: new Headers(),
1170
+ });
1171
+ await client.rawCompletionStream({
1172
+ system_prompt: "System.",
1173
+ message: "User.",
1174
+ });
1175
+ const calls = global.fetch.mock.calls;
1176
+ const dataCall = calls[1];
1177
+ (0, vitest_1.expect)(dataCall[0]).toContain("/api/chat/complete/stream");
1178
+ (0, vitest_1.expect)(dataCall[1]?.headers?.Accept).toBe("text/event-stream");
1179
+ });
1180
+ });
1181
+ // ============================================================================
1088
1182
  // Token Management Tests
1089
1183
  // ============================================================================
1090
1184
  (0, vitest_1.describe)("refreshToken", () => {
@@ -1161,3 +1255,719 @@ function mockErrorResponse(status, message) {
1161
1255
  (0, vitest_1.expect)(dataCall[0]).toBe("http://localhost:8080/api/find/users/123");
1162
1256
  });
1163
1257
  });
1258
+ // ============================================================================
1259
+ // Goal CRUD Tests
1260
+ // ============================================================================
1261
+ (0, vitest_1.describe)("EkoDBClient goals", () => {
1262
+ (0, vitest_1.it)("creates a goal", async () => {
1263
+ const client = createTestClient();
1264
+ mockTokenResponse();
1265
+ mockJsonResponse({ id: "goal_1", title: "Test Goal", status: "active" });
1266
+ const result = await client.goalCreate({ title: "Test Goal" });
1267
+ (0, vitest_1.expect)(result).toHaveProperty("id", "goal_1");
1268
+ (0, vitest_1.expect)(result).toHaveProperty("status", "active");
1269
+ });
1270
+ (0, vitest_1.it)("lists goals", async () => {
1271
+ const client = createTestClient();
1272
+ mockTokenResponse();
1273
+ mockJsonResponse({ goals: [{ id: "goal_1" }, { id: "goal_2" }] });
1274
+ const result = await client.goalList();
1275
+ (0, vitest_1.expect)(result).toHaveProperty("goals");
1276
+ });
1277
+ (0, vitest_1.it)("gets a goal by ID", async () => {
1278
+ const client = createTestClient();
1279
+ mockTokenResponse();
1280
+ mockJsonResponse({ id: "goal_1", title: "Test Goal" });
1281
+ const result = await client.goalGet("goal_1");
1282
+ (0, vitest_1.expect)(result).toHaveProperty("id", "goal_1");
1283
+ });
1284
+ (0, vitest_1.it)("updates a goal", async () => {
1285
+ const client = createTestClient();
1286
+ mockTokenResponse();
1287
+ mockJsonResponse({ id: "goal_1", title: "Updated" });
1288
+ const result = await client.goalUpdate("goal_1", { title: "Updated" });
1289
+ (0, vitest_1.expect)(result).toHaveProperty("title", "Updated");
1290
+ });
1291
+ (0, vitest_1.it)("deletes a goal", async () => {
1292
+ const client = createTestClient();
1293
+ mockTokenResponse();
1294
+ mockJsonResponse({});
1295
+ await (0, vitest_1.expect)(client.goalDelete("goal_1")).resolves.not.toThrow();
1296
+ });
1297
+ (0, vitest_1.it)("searches goals", async () => {
1298
+ const client = createTestClient();
1299
+ mockTokenResponse();
1300
+ mockJsonResponse({ goals: [{ id: "goal_1" }] });
1301
+ const result = await client.goalSearch("test query");
1302
+ (0, vitest_1.expect)(result).toHaveProperty("goals");
1303
+ });
1304
+ (0, vitest_1.it)("completes a goal", async () => {
1305
+ const client = createTestClient();
1306
+ mockTokenResponse();
1307
+ mockJsonResponse({ id: "goal_1", status: "pending_review" });
1308
+ const result = await client.goalComplete("goal_1", { summary: "Done" });
1309
+ (0, vitest_1.expect)(result).toHaveProperty("status", "pending_review");
1310
+ });
1311
+ (0, vitest_1.it)("approves a goal", async () => {
1312
+ const client = createTestClient();
1313
+ mockTokenResponse();
1314
+ mockJsonResponse({ id: "goal_1", status: "in_progress" });
1315
+ const result = await client.goalApprove("goal_1");
1316
+ (0, vitest_1.expect)(result).toHaveProperty("status", "in_progress");
1317
+ });
1318
+ (0, vitest_1.it)("rejects a goal", async () => {
1319
+ const client = createTestClient();
1320
+ mockTokenResponse();
1321
+ mockJsonResponse({ id: "goal_1", status: "failed" });
1322
+ const result = await client.goalReject("goal_1", { reason: "Bad plan" });
1323
+ (0, vitest_1.expect)(result).toHaveProperty("status", "failed");
1324
+ });
1325
+ (0, vitest_1.it)("starts a goal step", async () => {
1326
+ const client = createTestClient();
1327
+ mockTokenResponse();
1328
+ mockJsonResponse({ id: "goal_1" });
1329
+ const result = await client.goalStepStart("goal_1", 0);
1330
+ (0, vitest_1.expect)(result).toHaveProperty("id", "goal_1");
1331
+ });
1332
+ (0, vitest_1.it)("completes a goal step", async () => {
1333
+ const client = createTestClient();
1334
+ mockTokenResponse();
1335
+ mockJsonResponse({ id: "goal_1" });
1336
+ const result = await client.goalStepComplete("goal_1", 0, {
1337
+ result: "Step done",
1338
+ });
1339
+ (0, vitest_1.expect)(result).toHaveProperty("id", "goal_1");
1340
+ });
1341
+ (0, vitest_1.it)("fails a goal step", async () => {
1342
+ const client = createTestClient();
1343
+ mockTokenResponse();
1344
+ mockJsonResponse({ id: "goal_1" });
1345
+ const result = await client.goalStepFail("goal_1", 0, {
1346
+ error: "Step failed",
1347
+ });
1348
+ (0, vitest_1.expect)(result).toHaveProperty("id", "goal_1");
1349
+ });
1350
+ (0, vitest_1.it)("returns error for non-existent goal", async () => {
1351
+ const client = createTestClient();
1352
+ mockTokenResponse();
1353
+ mockErrorResponse(404, "Not Found");
1354
+ await (0, vitest_1.expect)(client.goalGet("nonexistent")).rejects.toThrow();
1355
+ });
1356
+ });
1357
+ // ============================================================================
1358
+ // Task CRUD Tests
1359
+ // ============================================================================
1360
+ (0, vitest_1.describe)("EkoDBClient tasks", () => {
1361
+ (0, vitest_1.it)("creates a task", async () => {
1362
+ const client = createTestClient();
1363
+ mockTokenResponse();
1364
+ mockJsonResponse({ id: "task_1", name: "Test Task", status: "active" });
1365
+ const result = await client.taskCreate({
1366
+ name: "Test Task",
1367
+ cron: "0 * * * *",
1368
+ });
1369
+ (0, vitest_1.expect)(result).toHaveProperty("id", "task_1");
1370
+ });
1371
+ (0, vitest_1.it)("lists tasks", async () => {
1372
+ const client = createTestClient();
1373
+ mockTokenResponse();
1374
+ mockJsonResponse({ tasks: [] });
1375
+ const result = await client.taskList();
1376
+ (0, vitest_1.expect)(result).toHaveProperty("tasks");
1377
+ });
1378
+ (0, vitest_1.it)("gets a task by ID", async () => {
1379
+ const client = createTestClient();
1380
+ mockTokenResponse();
1381
+ mockJsonResponse({ id: "task_1", name: "Test Task" });
1382
+ const result = await client.taskGet("task_1");
1383
+ (0, vitest_1.expect)(result).toHaveProperty("id", "task_1");
1384
+ });
1385
+ (0, vitest_1.it)("updates a task", async () => {
1386
+ const client = createTestClient();
1387
+ mockTokenResponse();
1388
+ mockJsonResponse({ id: "task_1", name: "Updated" });
1389
+ const result = await client.taskUpdate("task_1", { name: "Updated" });
1390
+ (0, vitest_1.expect)(result).toHaveProperty("name", "Updated");
1391
+ });
1392
+ (0, vitest_1.it)("deletes a task", async () => {
1393
+ const client = createTestClient();
1394
+ mockTokenResponse();
1395
+ mockJsonResponse({});
1396
+ await (0, vitest_1.expect)(client.taskDelete("task_1")).resolves.not.toThrow();
1397
+ });
1398
+ (0, vitest_1.it)("gets due tasks", async () => {
1399
+ const client = createTestClient();
1400
+ mockTokenResponse();
1401
+ mockJsonResponse({ tasks: [] });
1402
+ const result = await client.taskDue("2026-03-20T00:00:00Z");
1403
+ (0, vitest_1.expect)(result).toHaveProperty("tasks");
1404
+ });
1405
+ (0, vitest_1.it)("starts a task", async () => {
1406
+ const client = createTestClient();
1407
+ mockTokenResponse();
1408
+ mockJsonResponse({ id: "task_1", status: "running" });
1409
+ const result = await client.taskStart("task_1");
1410
+ (0, vitest_1.expect)(result).toHaveProperty("status", "running");
1411
+ });
1412
+ (0, vitest_1.it)("succeeds a task", async () => {
1413
+ const client = createTestClient();
1414
+ mockTokenResponse();
1415
+ mockJsonResponse({ id: "task_1", status: "active" });
1416
+ const result = await client.taskSucceed("task_1", { output: "Success" });
1417
+ (0, vitest_1.expect)(result).toHaveProperty("status", "active");
1418
+ });
1419
+ (0, vitest_1.it)("fails a task", async () => {
1420
+ const client = createTestClient();
1421
+ mockTokenResponse();
1422
+ mockJsonResponse({ id: "task_1", status: "active" });
1423
+ const result = await client.taskFail("task_1", { error: "Timeout" });
1424
+ (0, vitest_1.expect)(result).toHaveProperty("status", "active");
1425
+ });
1426
+ (0, vitest_1.it)("pauses a task", async () => {
1427
+ const client = createTestClient();
1428
+ mockTokenResponse();
1429
+ mockJsonResponse({ id: "task_1", status: "paused" });
1430
+ const result = await client.taskPause("task_1");
1431
+ (0, vitest_1.expect)(result).toHaveProperty("status", "paused");
1432
+ });
1433
+ (0, vitest_1.it)("resumes a task", async () => {
1434
+ const client = createTestClient();
1435
+ mockTokenResponse();
1436
+ mockJsonResponse({ id: "task_1", status: "active" });
1437
+ const result = await client.taskResume("task_1", {});
1438
+ (0, vitest_1.expect)(result).toHaveProperty("status", "active");
1439
+ });
1440
+ });
1441
+ // ============================================================================
1442
+ // Agent CRUD Tests
1443
+ // ============================================================================
1444
+ (0, vitest_1.describe)("EkoDBClient agents", () => {
1445
+ (0, vitest_1.it)("creates an agent", async () => {
1446
+ const client = createTestClient();
1447
+ mockTokenResponse();
1448
+ mockJsonResponse({ id: "agent_1", name: "TestAgent" });
1449
+ const result = await client.agentCreate({
1450
+ name: "TestAgent",
1451
+ system_prompt: "You help.",
1452
+ });
1453
+ (0, vitest_1.expect)(result).toHaveProperty("name", "TestAgent");
1454
+ });
1455
+ (0, vitest_1.it)("lists agents", async () => {
1456
+ const client = createTestClient();
1457
+ mockTokenResponse();
1458
+ mockJsonResponse({ agents: [] });
1459
+ const result = await client.agentList();
1460
+ (0, vitest_1.expect)(result).toHaveProperty("agents");
1461
+ });
1462
+ (0, vitest_1.it)("gets an agent by ID", async () => {
1463
+ const client = createTestClient();
1464
+ mockTokenResponse();
1465
+ mockJsonResponse({ id: "agent_1", name: "TestAgent" });
1466
+ const result = await client.agentGet("agent_1");
1467
+ (0, vitest_1.expect)(result).toHaveProperty("id", "agent_1");
1468
+ });
1469
+ (0, vitest_1.it)("gets an agent by name", async () => {
1470
+ const client = createTestClient();
1471
+ mockTokenResponse();
1472
+ mockJsonResponse({ id: "agent_1", name: "TestAgent" });
1473
+ const result = await client.agentGetByName("TestAgent");
1474
+ (0, vitest_1.expect)(result).toHaveProperty("name", "TestAgent");
1475
+ });
1476
+ (0, vitest_1.it)("updates an agent", async () => {
1477
+ const client = createTestClient();
1478
+ mockTokenResponse();
1479
+ mockJsonResponse({ id: "agent_1", name: "Updated" });
1480
+ const result = await client.agentUpdate("agent_1", { name: "Updated" });
1481
+ (0, vitest_1.expect)(result).toHaveProperty("name", "Updated");
1482
+ });
1483
+ (0, vitest_1.it)("deletes an agent", async () => {
1484
+ const client = createTestClient();
1485
+ mockTokenResponse();
1486
+ mockJsonResponse({});
1487
+ await (0, vitest_1.expect)(client.agentDelete("agent_1")).resolves.not.toThrow();
1488
+ });
1489
+ (0, vitest_1.it)("gets agents by deployment", async () => {
1490
+ const client = createTestClient();
1491
+ mockTokenResponse();
1492
+ mockJsonResponse({ agents: [{ id: "agent_1" }] });
1493
+ const result = await client.agentsByDeployment("deploy_1");
1494
+ (0, vitest_1.expect)(result).toHaveProperty("agents");
1495
+ });
1496
+ (0, vitest_1.it)("returns error for non-existent agent", async () => {
1497
+ const client = createTestClient();
1498
+ mockTokenResponse();
1499
+ mockErrorResponse(404, "Not Found");
1500
+ await (0, vitest_1.expect)(client.agentGet("nonexistent")).rejects.toThrow();
1501
+ });
1502
+ });
1503
+ // ============================================================================
1504
+ // rawCompletionStreamWithProgress Tests
1505
+ // ============================================================================
1506
+ (0, vitest_1.describe)("EkoDBClient rawCompletionStreamWithProgress", () => {
1507
+ (0, vitest_1.it)("calls onToken for each token", async () => {
1508
+ const client = createTestClient();
1509
+ mockTokenResponse();
1510
+ // Mock SSE response
1511
+ const sseBody = [
1512
+ 'data: {"token": "Hello"}',
1513
+ 'data: {"token": " World"}',
1514
+ 'data: {"content": "Hello World"}',
1515
+ ].join("\n");
1516
+ mockFetch.mockResolvedValueOnce({
1517
+ ok: true,
1518
+ status: 200,
1519
+ text: async () => sseBody,
1520
+ headers: new Headers({ "content-type": "text/event-stream" }),
1521
+ });
1522
+ const tokens = [];
1523
+ const result = await client.rawCompletionStreamWithProgress({ system_prompt: "test", message: "test" }, (token) => tokens.push(token));
1524
+ (0, vitest_1.expect)(tokens).toEqual(["Hello", " World"]);
1525
+ (0, vitest_1.expect)(result.content).toBe("Hello World");
1526
+ });
1527
+ (0, vitest_1.it)("throws on SSE error event", async () => {
1528
+ const client = createTestClient();
1529
+ mockTokenResponse();
1530
+ const sseBody = 'data: {"error": "Model overloaded"}';
1531
+ mockFetch.mockResolvedValueOnce({
1532
+ ok: true,
1533
+ status: 200,
1534
+ text: async () => sseBody,
1535
+ headers: new Headers({ "content-type": "text/event-stream" }),
1536
+ });
1537
+ const tokens = [];
1538
+ await (0, vitest_1.expect)(client.rawCompletionStreamWithProgress({ system_prompt: "test", message: "test" }, (token) => tokens.push(token))).rejects.toThrow("Model overloaded");
1539
+ });
1540
+ });
1541
+ // ============================================================================
1542
+ // Goal Template CRUD Tests
1543
+ // ============================================================================
1544
+ (0, vitest_1.describe)("EkoDBClient goal templates", () => {
1545
+ (0, vitest_1.it)("creates a goal template", async () => {
1546
+ const client = createTestClient();
1547
+ mockTokenResponse();
1548
+ mockJsonResponse({ id: "gt_1", title: "Deploy Checklist" });
1549
+ const result = await client.goalTemplateCreate({
1550
+ title: "Deploy Checklist",
1551
+ steps: [{ title: "Run tests" }],
1552
+ });
1553
+ (0, vitest_1.expect)(result).toHaveProperty("id", "gt_1");
1554
+ (0, vitest_1.expect)(result).toHaveProperty("title", "Deploy Checklist");
1555
+ });
1556
+ (0, vitest_1.it)("creates a goal template and verifies POST method", async () => {
1557
+ const client = createTestClient();
1558
+ mockTokenResponse();
1559
+ mockJsonResponse({ id: "gt_2", title: "Onboarding" });
1560
+ await client.goalTemplateCreate({ title: "Onboarding" });
1561
+ const calls = global.fetch.mock.calls;
1562
+ const dataCall = calls[1]; // calls[0] is token
1563
+ (0, vitest_1.expect)(dataCall[0]).toContain("/api/chat/goal-templates");
1564
+ (0, vitest_1.expect)(dataCall[1]?.method).toBe("POST");
1565
+ });
1566
+ (0, vitest_1.it)("lists goal templates", async () => {
1567
+ const client = createTestClient();
1568
+ mockTokenResponse();
1569
+ mockJsonResponse({
1570
+ templates: [{ id: "gt_1" }, { id: "gt_2" }],
1571
+ });
1572
+ const result = await client.goalTemplateList();
1573
+ (0, vitest_1.expect)(result).toHaveProperty("templates");
1574
+ });
1575
+ (0, vitest_1.it)("lists goal templates and verifies GET method", async () => {
1576
+ const client = createTestClient();
1577
+ mockTokenResponse();
1578
+ mockJsonResponse({ templates: [] });
1579
+ await client.goalTemplateList();
1580
+ const calls = global.fetch.mock.calls;
1581
+ const dataCall = calls[1];
1582
+ (0, vitest_1.expect)(dataCall[0]).toContain("/api/chat/goal-templates");
1583
+ (0, vitest_1.expect)(dataCall[1]?.method).toBe("GET");
1584
+ });
1585
+ (0, vitest_1.it)("gets a goal template by ID", async () => {
1586
+ const client = createTestClient();
1587
+ mockTokenResponse();
1588
+ mockJsonResponse({ id: "gt_1", title: "Deploy Checklist" });
1589
+ const result = await client.goalTemplateGet("gt_1");
1590
+ (0, vitest_1.expect)(result).toHaveProperty("id", "gt_1");
1591
+ (0, vitest_1.expect)(result).toHaveProperty("title", "Deploy Checklist");
1592
+ });
1593
+ (0, vitest_1.it)("gets a goal template and verifies endpoint", async () => {
1594
+ const client = createTestClient();
1595
+ mockTokenResponse();
1596
+ mockJsonResponse({ id: "gt_1", title: "Deploy Checklist" });
1597
+ await client.goalTemplateGet("gt_1");
1598
+ const calls = global.fetch.mock.calls;
1599
+ const dataCall = calls[1];
1600
+ (0, vitest_1.expect)(dataCall[0]).toContain("/api/chat/goal-templates/gt_1");
1601
+ (0, vitest_1.expect)(dataCall[1]?.method).toBe("GET");
1602
+ });
1603
+ (0, vitest_1.it)("updates a goal template", async () => {
1604
+ const client = createTestClient();
1605
+ mockTokenResponse();
1606
+ mockJsonResponse({ id: "gt_1", title: "Updated Checklist" });
1607
+ const result = await client.goalTemplateUpdate("gt_1", {
1608
+ title: "Updated Checklist",
1609
+ });
1610
+ (0, vitest_1.expect)(result).toHaveProperty("title", "Updated Checklist");
1611
+ });
1612
+ (0, vitest_1.it)("updates a goal template and verifies PUT method", async () => {
1613
+ const client = createTestClient();
1614
+ mockTokenResponse();
1615
+ mockJsonResponse({ id: "gt_1", title: "Updated" });
1616
+ await client.goalTemplateUpdate("gt_1", { title: "Updated" });
1617
+ const calls = global.fetch.mock.calls;
1618
+ const dataCall = calls[1];
1619
+ (0, vitest_1.expect)(dataCall[0]).toContain("/api/chat/goal-templates/gt_1");
1620
+ (0, vitest_1.expect)(dataCall[1]?.method).toBe("PUT");
1621
+ });
1622
+ (0, vitest_1.it)("deletes a goal template", async () => {
1623
+ const client = createTestClient();
1624
+ mockTokenResponse();
1625
+ mockJsonResponse({});
1626
+ await (0, vitest_1.expect)(client.goalTemplateDelete("gt_1")).resolves.not.toThrow();
1627
+ });
1628
+ (0, vitest_1.it)("deletes a goal template and verifies DELETE method", async () => {
1629
+ const client = createTestClient();
1630
+ mockTokenResponse();
1631
+ mockJsonResponse({});
1632
+ await client.goalTemplateDelete("gt_1");
1633
+ const calls = global.fetch.mock.calls;
1634
+ const dataCall = calls[1];
1635
+ (0, vitest_1.expect)(dataCall[0]).toContain("/api/chat/goal-templates/gt_1");
1636
+ (0, vitest_1.expect)(dataCall[1]?.method).toBe("DELETE");
1637
+ });
1638
+ (0, vitest_1.it)("returns error for non-existent goal template", async () => {
1639
+ const client = createTestClient();
1640
+ mockTokenResponse();
1641
+ mockErrorResponse(404, "Not Found");
1642
+ await (0, vitest_1.expect)(client.goalTemplateGet("nonexistent")).rejects.toThrow();
1643
+ });
1644
+ });
1645
+ // ============================================================================
1646
+ // chatMessageStream (SSE) Tests
1647
+ // ============================================================================
1648
+ (0, vitest_1.describe)("EkoDBClient chatMessageStream", () => {
1649
+ (0, vitest_1.it)("emits chunk and end events from SSE stream", async () => {
1650
+ const client = createTestClient();
1651
+ mockTokenResponse();
1652
+ const sseBody = [
1653
+ 'data: {"token":"Hello"}',
1654
+ 'data: {"token":" world"}',
1655
+ 'data: {"content":"Hello world","message_id":"msg_1","execution_time_ms":42}',
1656
+ "",
1657
+ ].join("\n");
1658
+ mockFetch.mockResolvedValueOnce({
1659
+ ok: true,
1660
+ status: 200,
1661
+ text: async () => sseBody,
1662
+ headers: new Headers({ "content-type": "text/event-stream" }),
1663
+ });
1664
+ const events = [];
1665
+ const stream = client.chatMessageStream("chat_123", {
1666
+ message: "Hello",
1667
+ });
1668
+ stream.on("event", (evt) => events.push(evt));
1669
+ // Wait for the async SSE processing to complete
1670
+ await new Promise((resolve) => setTimeout(resolve, 50));
1671
+ (0, vitest_1.expect)(events).toHaveLength(3);
1672
+ (0, vitest_1.expect)(events[0]).toEqual({ type: "chunk", content: "Hello" });
1673
+ (0, vitest_1.expect)(events[1]).toEqual({ type: "chunk", content: " world" });
1674
+ (0, vitest_1.expect)(events[2].type).toBe("end");
1675
+ (0, vitest_1.expect)(events[2].messageId).toBe("msg_1");
1676
+ (0, vitest_1.expect)(events[2].executionTimeMs).toBe(42);
1677
+ });
1678
+ (0, vitest_1.it)("emits error event on SSE error", async () => {
1679
+ const client = createTestClient();
1680
+ mockTokenResponse();
1681
+ const sseBody = 'data: {"error":"LLM timeout"}\n';
1682
+ mockFetch.mockResolvedValueOnce({
1683
+ ok: true,
1684
+ status: 200,
1685
+ text: async () => sseBody,
1686
+ headers: new Headers({ "content-type": "text/event-stream" }),
1687
+ });
1688
+ const events = [];
1689
+ const stream = client.chatMessageStream("chat_123", {
1690
+ message: "Hello",
1691
+ });
1692
+ stream.on("event", (evt) => events.push(evt));
1693
+ await new Promise((resolve) => setTimeout(resolve, 50));
1694
+ (0, vitest_1.expect)(events).toHaveLength(1);
1695
+ (0, vitest_1.expect)(events[0]).toEqual({ type: "error", error: "LLM timeout" });
1696
+ });
1697
+ (0, vitest_1.it)("emits error event on non-200 HTTP response", async () => {
1698
+ const client = createTestClient();
1699
+ mockTokenResponse();
1700
+ mockFetch.mockResolvedValueOnce({
1701
+ ok: false,
1702
+ status: 401,
1703
+ text: async () => "Unauthorized",
1704
+ headers: new Headers(),
1705
+ });
1706
+ const events = [];
1707
+ const stream = client.chatMessageStream("chat_123", {
1708
+ message: "Hello",
1709
+ });
1710
+ stream.on("event", (evt) => events.push(evt));
1711
+ await new Promise((resolve) => setTimeout(resolve, 50));
1712
+ (0, vitest_1.expect)(events).toHaveLength(1);
1713
+ (0, vitest_1.expect)(events[0].type).toBe("error");
1714
+ (0, vitest_1.expect)(events[0].error).toContain("401");
1715
+ });
1716
+ (0, vitest_1.it)("calls the correct stream endpoint", async () => {
1717
+ const client = createTestClient();
1718
+ mockTokenResponse();
1719
+ mockFetch.mockResolvedValueOnce({
1720
+ ok: true,
1721
+ status: 200,
1722
+ text: async () => 'data: {"token":"ok"}\n',
1723
+ headers: new Headers({ "content-type": "text/event-stream" }),
1724
+ });
1725
+ client.chatMessageStream("chat_456", { message: "Test" });
1726
+ await new Promise((resolve) => setTimeout(resolve, 50));
1727
+ const calls = global.fetch.mock.calls;
1728
+ const dataCall = calls[1];
1729
+ (0, vitest_1.expect)(dataCall[0]).toContain("/api/chat/chat_456/messages/stream");
1730
+ (0, vitest_1.expect)(dataCall[1]?.method).toBe("POST");
1731
+ (0, vitest_1.expect)(dataCall[1]?.headers?.Accept).toBe("text/event-stream");
1732
+ });
1733
+ });
1734
+ // ============================================================================
1735
+ // Schedule CRUD Tests
1736
+ // ============================================================================
1737
+ (0, vitest_1.describe)("EkoDBClient schedules", () => {
1738
+ (0, vitest_1.it)("creates a schedule", async () => {
1739
+ const client = createTestClient();
1740
+ mockTokenResponse();
1741
+ mockJsonResponse({
1742
+ id: "sched_1",
1743
+ name: "Nightly Backup",
1744
+ cron: "0 2 * * *",
1745
+ status: "active",
1746
+ });
1747
+ const result = await client.createSchedule({
1748
+ name: "Nightly Backup",
1749
+ cron: "0 2 * * *",
1750
+ action: "backup",
1751
+ });
1752
+ (0, vitest_1.expect)(result).toHaveProperty("id", "sched_1");
1753
+ (0, vitest_1.expect)(result).toHaveProperty("status", "active");
1754
+ });
1755
+ (0, vitest_1.it)("lists schedules", async () => {
1756
+ const client = createTestClient();
1757
+ mockTokenResponse();
1758
+ mockJsonResponse({
1759
+ schedules: [
1760
+ { id: "sched_1", name: "Nightly Backup" },
1761
+ { id: "sched_2", name: "Hourly Sync" },
1762
+ ],
1763
+ });
1764
+ const result = await client.listSchedules();
1765
+ (0, vitest_1.expect)(result).toHaveProperty("schedules");
1766
+ });
1767
+ (0, vitest_1.it)("gets a schedule by ID", async () => {
1768
+ const client = createTestClient();
1769
+ mockTokenResponse();
1770
+ mockJsonResponse({
1771
+ id: "sched_1",
1772
+ name: "Nightly Backup",
1773
+ cron: "0 2 * * *",
1774
+ });
1775
+ const result = await client.getSchedule("sched_1");
1776
+ (0, vitest_1.expect)(result).toHaveProperty("id", "sched_1");
1777
+ (0, vitest_1.expect)(result).toHaveProperty("name", "Nightly Backup");
1778
+ });
1779
+ (0, vitest_1.it)("updates a schedule", async () => {
1780
+ const client = createTestClient();
1781
+ mockTokenResponse();
1782
+ mockJsonResponse({
1783
+ id: "sched_1",
1784
+ name: "Updated Backup",
1785
+ cron: "0 3 * * *",
1786
+ });
1787
+ const result = await client.updateSchedule("sched_1", {
1788
+ name: "Updated Backup",
1789
+ cron: "0 3 * * *",
1790
+ });
1791
+ (0, vitest_1.expect)(result).toHaveProperty("name", "Updated Backup");
1792
+ (0, vitest_1.expect)(result).toHaveProperty("cron", "0 3 * * *");
1793
+ });
1794
+ (0, vitest_1.it)("deletes a schedule", async () => {
1795
+ const client = createTestClient();
1796
+ mockTokenResponse();
1797
+ mockJsonResponse({});
1798
+ await (0, vitest_1.expect)(client.deleteSchedule("sched_1")).resolves.not.toThrow();
1799
+ });
1800
+ (0, vitest_1.it)("pauses a schedule", async () => {
1801
+ const client = createTestClient();
1802
+ mockTokenResponse();
1803
+ mockJsonResponse({ id: "sched_1", status: "paused" });
1804
+ const result = await client.pauseSchedule("sched_1");
1805
+ (0, vitest_1.expect)(result).toHaveProperty("status", "paused");
1806
+ });
1807
+ (0, vitest_1.it)("resumes a schedule", async () => {
1808
+ const client = createTestClient();
1809
+ mockTokenResponse();
1810
+ mockJsonResponse({ id: "sched_1", status: "active" });
1811
+ const result = await client.resumeSchedule("sched_1");
1812
+ (0, vitest_1.expect)(result).toHaveProperty("status", "active");
1813
+ });
1814
+ });
1815
+ // ============================================================================
1816
+ // KV Links Tests
1817
+ // ============================================================================
1818
+ (0, vitest_1.describe)("EkoDBClient kv links", () => {
1819
+ (0, vitest_1.it)("gets links for a KV key", async () => {
1820
+ const client = createTestClient();
1821
+ mockTokenResponse();
1822
+ mockJsonResponse({
1823
+ links: [
1824
+ { collection: "users", document_id: "user_1" },
1825
+ { collection: "orders", document_id: "order_1" },
1826
+ ],
1827
+ });
1828
+ const result = await client.kvGetLinks("session:user123");
1829
+ (0, vitest_1.expect)(result).toHaveProperty("links");
1830
+ });
1831
+ (0, vitest_1.it)("links a document to a KV key", async () => {
1832
+ const client = createTestClient();
1833
+ mockTokenResponse();
1834
+ mockJsonResponse({ status: "linked" });
1835
+ const result = await client.kvLink("session:user123", "users", "user_1");
1836
+ (0, vitest_1.expect)(result).toHaveProperty("status", "linked");
1837
+ });
1838
+ (0, vitest_1.it)("unlinks a document from a KV key", async () => {
1839
+ const client = createTestClient();
1840
+ mockTokenResponse();
1841
+ mockJsonResponse({ status: "unlinked" });
1842
+ const result = await client.kvUnlink("session:user123", "users", "user_1");
1843
+ (0, vitest_1.expect)(result).toHaveProperty("status", "unlinked");
1844
+ });
1845
+ });
1846
+ // ============================================================================
1847
+ // Text Search and Hybrid Search Tests
1848
+ // ============================================================================
1849
+ (0, vitest_1.describe)("EkoDBClient text and hybrid search", () => {
1850
+ (0, vitest_1.it)("performs text search", async () => {
1851
+ const client = createTestClient();
1852
+ mockTokenResponse();
1853
+ mockJsonResponse({
1854
+ results: [
1855
+ {
1856
+ record: { id: "doc_1", title: "Ownership Guide" },
1857
+ score: 0.92,
1858
+ matched_fields: ["title"],
1859
+ },
1860
+ {
1861
+ record: { id: "doc_2", title: "Property Ownership" },
1862
+ score: 0.85,
1863
+ matched_fields: ["title"],
1864
+ },
1865
+ ],
1866
+ total: 2,
1867
+ took_ms: 12,
1868
+ });
1869
+ const result = await client.textSearch("documents", "ownership", {
1870
+ limit: 10,
1871
+ select_fields: ["title", "content"],
1872
+ });
1873
+ (0, vitest_1.expect)(result.results).toHaveLength(2);
1874
+ (0, vitest_1.expect)(result.total).toBe(2);
1875
+ (0, vitest_1.expect)(result.results[0].score).toBe(0.92);
1876
+ });
1877
+ (0, vitest_1.it)("performs hybrid search", async () => {
1878
+ const client = createTestClient();
1879
+ mockTokenResponse();
1880
+ mockJsonResponse({
1881
+ results: [
1882
+ {
1883
+ record: { id: "doc_1", title: "ML Guide", content: "Learn ML" },
1884
+ score: 0.95,
1885
+ matched_fields: ["title", "content"],
1886
+ },
1887
+ ],
1888
+ total: 1,
1889
+ took_ms: 25,
1890
+ });
1891
+ const queryVector = [0.1, 0.2, 0.3, 0.4, 0.5];
1892
+ const result = await client.hybridSearch("documents", "machine learning", queryVector, 5);
1893
+ (0, vitest_1.expect)(result).toHaveLength(1);
1894
+ (0, vitest_1.expect)(result[0]).toHaveProperty("id", "doc_1");
1895
+ (0, vitest_1.expect)(result[0]).toHaveProperty("title", "ML Guide");
1896
+ });
1897
+ });
1898
+ // ============================================================================
1899
+ // Auth Token Management
1900
+ // ============================================================================
1901
+ (0, vitest_1.describe)("EkoDBClient auth token management", () => {
1902
+ (0, vitest_1.beforeEach)(() => {
1903
+ mockFetch.mockReset();
1904
+ });
1905
+ (0, vitest_1.it)("getToken auto-refreshes when token is about to expire", async () => {
1906
+ const client = createTestClient();
1907
+ // First init — get a token with a short expiry (30s from now)
1908
+ const shortExp = Math.floor(Date.now() / 1000) + 30;
1909
+ const shortPayload = btoa(JSON.stringify({ exp: shortExp }))
1910
+ .replace(/\+/g, "-")
1911
+ .replace(/\//g, "_")
1912
+ .replace(/=/g, "");
1913
+ const shortJwt = `eyJhbGciOiJIUzI1NiJ9.${shortPayload}.fakesig`;
1914
+ mockFetch.mockResolvedValueOnce({
1915
+ ok: true,
1916
+ status: 200,
1917
+ json: async () => ({ token: shortJwt }),
1918
+ headers: new Headers(),
1919
+ });
1920
+ await client.init();
1921
+ // Now getToken should see it's about to expire and refresh
1922
+ const newExp = Math.floor(Date.now() / 1000) + 3600;
1923
+ const newPayload = btoa(JSON.stringify({ exp: newExp }))
1924
+ .replace(/\+/g, "-")
1925
+ .replace(/\//g, "_")
1926
+ .replace(/=/g, "");
1927
+ const newJwt = `eyJhbGciOiJIUzI1NiJ9.${newPayload}.fakesig`;
1928
+ mockFetch.mockResolvedValueOnce({
1929
+ ok: true,
1930
+ status: 200,
1931
+ json: async () => ({ token: newJwt }),
1932
+ headers: new Headers(),
1933
+ });
1934
+ const token = await client.getToken();
1935
+ (0, vitest_1.expect)(token).toBe(newJwt);
1936
+ // 2 fetch calls: init + proactive refresh
1937
+ (0, vitest_1.expect)(mockFetch).toHaveBeenCalledTimes(2);
1938
+ });
1939
+ (0, vitest_1.it)("getToken returns cached token when not expired", async () => {
1940
+ const client = createTestClient();
1941
+ // Token with expiry far in the future
1942
+ const farExp = Math.floor(Date.now() / 1000) + 7200;
1943
+ const payload = btoa(JSON.stringify({ exp: farExp }))
1944
+ .replace(/\+/g, "-")
1945
+ .replace(/\//g, "_")
1946
+ .replace(/=/g, "");
1947
+ const jwt = `eyJhbGciOiJIUzI1NiJ9.${payload}.fakesig`;
1948
+ mockFetch.mockResolvedValueOnce({
1949
+ ok: true,
1950
+ status: 200,
1951
+ json: async () => ({ token: jwt }),
1952
+ headers: new Headers(),
1953
+ });
1954
+ await client.init();
1955
+ // getToken should return cached — no extra fetch
1956
+ const token = await client.getToken();
1957
+ (0, vitest_1.expect)(token).toBe(jwt);
1958
+ (0, vitest_1.expect)(mockFetch).toHaveBeenCalledTimes(1); // only init
1959
+ });
1960
+ (0, vitest_1.it)("clearTokenCache resets token and expiry", async () => {
1961
+ const client = createTestClient();
1962
+ mockTokenResponse();
1963
+ await client.init();
1964
+ (0, vitest_1.expect)(await client.getToken()).toBeTruthy();
1965
+ client.clearTokenCache();
1966
+ // After clear, getToken will auto-refresh (fetch a new token)
1967
+ mockTokenResponse();
1968
+ const token = await client.getToken();
1969
+ (0, vitest_1.expect)(token).toBe("test-jwt-token");
1970
+ // 2 fetch calls: init + post-clear getToken refresh
1971
+ (0, vitest_1.expect)(mockFetch).toHaveBeenCalledTimes(2);
1972
+ });
1973
+ });