@ekodb/ekodb-client 0.7.1 → 0.8.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.
@@ -0,0 +1,702 @@
1
+ "use strict";
2
+ /**
3
+ * Unit tests for ekoDB TypeScript client
4
+ *
5
+ * These tests use vitest and mock fetch to test client methods
6
+ * without requiring a running ekoDB server.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ const vitest_1 = require("vitest");
10
+ const client_1 = require("./client");
11
+ // Mock fetch globally
12
+ const mockFetch = vitest_1.vi.fn();
13
+ global.fetch = mockFetch;
14
+ // ============================================================================
15
+ // Test Helpers
16
+ // ============================================================================
17
+ function createTestClient() {
18
+ return new client_1.EkoDBClient({
19
+ baseURL: "http://localhost:8080",
20
+ apiKey: "test-api-key",
21
+ format: client_1.SerializationFormat.Json,
22
+ shouldRetry: false,
23
+ });
24
+ }
25
+ function mockTokenResponse() {
26
+ mockFetch.mockResolvedValueOnce({
27
+ ok: true,
28
+ status: 200,
29
+ json: async () => ({ token: "test-jwt-token" }),
30
+ headers: new Headers(),
31
+ });
32
+ }
33
+ function mockJsonResponse(data, status = 200) {
34
+ mockFetch.mockResolvedValueOnce({
35
+ ok: status >= 200 && status < 300,
36
+ status,
37
+ json: async () => data,
38
+ text: async () => JSON.stringify(data),
39
+ headers: new Headers({
40
+ "content-type": "application/json",
41
+ }),
42
+ });
43
+ }
44
+ function mockErrorResponse(status, message) {
45
+ mockFetch.mockResolvedValueOnce({
46
+ ok: false,
47
+ status,
48
+ json: async () => ({ error: message }),
49
+ text: async () => message,
50
+ headers: new Headers(),
51
+ });
52
+ }
53
+ // ============================================================================
54
+ // Setup/Teardown
55
+ // ============================================================================
56
+ (0, vitest_1.beforeEach)(() => {
57
+ mockFetch.mockReset();
58
+ });
59
+ (0, vitest_1.afterEach)(() => {
60
+ vitest_1.vi.clearAllMocks();
61
+ });
62
+ // ============================================================================
63
+ // Client Configuration Tests
64
+ // ============================================================================
65
+ (0, vitest_1.describe)("EkoDBClient configuration", () => {
66
+ (0, vitest_1.it)("creates client with required config", () => {
67
+ const client = new client_1.EkoDBClient({
68
+ baseURL: "http://localhost:8080",
69
+ apiKey: "test-key",
70
+ });
71
+ (0, vitest_1.expect)(client).toBeInstanceOf(client_1.EkoDBClient);
72
+ });
73
+ (0, vitest_1.it)("creates client with all options", () => {
74
+ const client = new client_1.EkoDBClient({
75
+ baseURL: "http://localhost:8080",
76
+ apiKey: "test-key",
77
+ timeout: 60000,
78
+ maxRetries: 5,
79
+ shouldRetry: true,
80
+ format: client_1.SerializationFormat.Json,
81
+ });
82
+ (0, vitest_1.expect)(client).toBeInstanceOf(client_1.EkoDBClient);
83
+ });
84
+ });
85
+ // ============================================================================
86
+ // Health Check Tests
87
+ // ============================================================================
88
+ // Note: Health check tests require specific response format matching
89
+ // Covered by integration tests
90
+ // ============================================================================
91
+ // Insert Tests
92
+ // ============================================================================
93
+ (0, vitest_1.describe)("EkoDBClient insert", () => {
94
+ (0, vitest_1.it)("inserts record successfully", async () => {
95
+ const client = createTestClient();
96
+ mockTokenResponse();
97
+ mockJsonResponse({ id: "record_123", name: "Test" });
98
+ const result = await client.insert("users", { name: "Test" });
99
+ (0, vitest_1.expect)(result).toHaveProperty("id", "record_123");
100
+ (0, vitest_1.expect)(mockFetch).toHaveBeenCalledTimes(2);
101
+ });
102
+ (0, vitest_1.it)("inserts record with TTL", async () => {
103
+ const client = createTestClient();
104
+ mockTokenResponse();
105
+ mockJsonResponse({ id: "record_123", name: "Test", ttl: "30m" });
106
+ const result = await client.insert("users", { name: "Test", ttl: "30m" });
107
+ (0, vitest_1.expect)(result).toHaveProperty("id");
108
+ });
109
+ });
110
+ // ============================================================================
111
+ // Find Tests
112
+ // ============================================================================
113
+ (0, vitest_1.describe)("EkoDBClient find", () => {
114
+ (0, vitest_1.it)("finds records with query", async () => {
115
+ const client = createTestClient();
116
+ mockTokenResponse();
117
+ mockJsonResponse([
118
+ { id: "user_1", name: "Alice" },
119
+ { id: "user_2", name: "Bob" },
120
+ ]);
121
+ const result = await client.find("users", { limit: 10 });
122
+ (0, vitest_1.expect)(result).toHaveLength(2);
123
+ (0, vitest_1.expect)(result[0]).toHaveProperty("name", "Alice");
124
+ });
125
+ (0, vitest_1.it)("finds record by ID", async () => {
126
+ const client = createTestClient();
127
+ mockTokenResponse();
128
+ mockJsonResponse({ id: "user_123", name: "Alice" });
129
+ const result = await client.findById("users", "user_123");
130
+ (0, vitest_1.expect)(result).toHaveProperty("id", "user_123");
131
+ });
132
+ (0, vitest_1.it)("throws error for not found record", async () => {
133
+ const client = createTestClient();
134
+ mockTokenResponse();
135
+ mockErrorResponse(404, "Not found");
136
+ await (0, vitest_1.expect)(client.findById("users", "nonexistent")).rejects.toThrow();
137
+ });
138
+ });
139
+ // ============================================================================
140
+ // Update Tests
141
+ // ============================================================================
142
+ (0, vitest_1.describe)("EkoDBClient update", () => {
143
+ (0, vitest_1.it)("updates record successfully", async () => {
144
+ const client = createTestClient();
145
+ mockTokenResponse();
146
+ mockJsonResponse({ id: "user_123", name: "Updated" });
147
+ const result = await client.update("users", "user_123", {
148
+ name: "Updated",
149
+ });
150
+ (0, vitest_1.expect)(result).toHaveProperty("name", "Updated");
151
+ });
152
+ });
153
+ // ============================================================================
154
+ // Delete Tests
155
+ // ============================================================================
156
+ (0, vitest_1.describe)("EkoDBClient delete", () => {
157
+ (0, vitest_1.it)("deletes record successfully", async () => {
158
+ const client = createTestClient();
159
+ mockTokenResponse();
160
+ mockJsonResponse({ id: "user_123", deleted: true });
161
+ await (0, vitest_1.expect)(client.delete("users", "user_123")).resolves.not.toThrow();
162
+ });
163
+ });
164
+ // ============================================================================
165
+ // Batch Operations Tests
166
+ // ============================================================================
167
+ (0, vitest_1.describe)("EkoDBClient batch operations", () => {
168
+ (0, vitest_1.it)("batch inserts records", async () => {
169
+ const client = createTestClient();
170
+ mockTokenResponse();
171
+ mockJsonResponse({
172
+ successful: ["id_1", "id_2", "id_3"],
173
+ failed: [],
174
+ });
175
+ const records = [{ name: "A" }, { name: "B" }, { name: "C" }];
176
+ const result = await client.batchInsert("users", records);
177
+ (0, vitest_1.expect)(result.successful).toHaveLength(3);
178
+ (0, vitest_1.expect)(result.failed).toHaveLength(0);
179
+ });
180
+ (0, vitest_1.it)("batch deletes records", async () => {
181
+ const client = createTestClient();
182
+ mockTokenResponse();
183
+ mockJsonResponse({
184
+ successful: ["id_1", "id_2"],
185
+ failed: [],
186
+ });
187
+ const result = await client.batchDelete("users", ["id_1", "id_2"]);
188
+ (0, vitest_1.expect)(result.successful).toHaveLength(2);
189
+ });
190
+ });
191
+ // ============================================================================
192
+ // KV Store Tests
193
+ // ============================================================================
194
+ (0, vitest_1.describe)("EkoDBClient KV store", () => {
195
+ (0, vitest_1.it)("sets KV value", async () => {
196
+ const client = createTestClient();
197
+ mockTokenResponse();
198
+ mockJsonResponse({ success: true });
199
+ await (0, vitest_1.expect)(client.kvSet("my_key", { data: "value" })).resolves.not.toThrow();
200
+ });
201
+ (0, vitest_1.it)("gets KV value", async () => {
202
+ const client = createTestClient();
203
+ mockTokenResponse();
204
+ mockJsonResponse({ value: { data: "stored_value" } });
205
+ const result = await client.kvGet("my_key");
206
+ (0, vitest_1.expect)(result).toEqual({ data: "stored_value" });
207
+ });
208
+ (0, vitest_1.it)("deletes KV value", async () => {
209
+ const client = createTestClient();
210
+ mockTokenResponse();
211
+ mockJsonResponse({ deleted: true });
212
+ await (0, vitest_1.expect)(client.kvDelete("my_key")).resolves.not.toThrow();
213
+ });
214
+ (0, vitest_1.it)("checks KV exists", async () => {
215
+ const client = createTestClient();
216
+ mockTokenResponse();
217
+ mockJsonResponse({ value: "something" });
218
+ const result = await client.kvExists("my_key");
219
+ (0, vitest_1.expect)(result).toBe(true);
220
+ });
221
+ });
222
+ // ============================================================================
223
+ // Transaction Tests
224
+ // ============================================================================
225
+ (0, vitest_1.describe)("EkoDBClient transactions", () => {
226
+ (0, vitest_1.it)("begins transaction", async () => {
227
+ const client = createTestClient();
228
+ mockTokenResponse();
229
+ mockJsonResponse({ transaction_id: "tx_123456" });
230
+ const txId = await client.beginTransaction();
231
+ (0, vitest_1.expect)(txId).toBe("tx_123456");
232
+ });
233
+ (0, vitest_1.it)("commits transaction", async () => {
234
+ const client = createTestClient();
235
+ mockTokenResponse();
236
+ mockJsonResponse({ status: "committed" });
237
+ await (0, vitest_1.expect)(client.commitTransaction("tx_123")).resolves.not.toThrow();
238
+ });
239
+ (0, vitest_1.it)("rolls back transaction", async () => {
240
+ const client = createTestClient();
241
+ mockTokenResponse();
242
+ mockJsonResponse({ status: "rolled_back" });
243
+ await (0, vitest_1.expect)(client.rollbackTransaction("tx_123")).resolves.not.toThrow();
244
+ });
245
+ });
246
+ // ============================================================================
247
+ // Collection Management Tests
248
+ // ============================================================================
249
+ (0, vitest_1.describe)("EkoDBClient collections", () => {
250
+ (0, vitest_1.it)("lists collections", async () => {
251
+ const client = createTestClient();
252
+ mockTokenResponse();
253
+ mockJsonResponse({ collections: ["users", "posts", "comments"] });
254
+ const result = await client.listCollections();
255
+ (0, vitest_1.expect)(result).toContain("users");
256
+ (0, vitest_1.expect)(result).toHaveLength(3);
257
+ });
258
+ (0, vitest_1.it)("deletes collection", async () => {
259
+ const client = createTestClient();
260
+ mockTokenResponse();
261
+ mockJsonResponse({ status: "deleted" });
262
+ await (0, vitest_1.expect)(client.deleteCollection("test_collection")).resolves.not.toThrow();
263
+ });
264
+ });
265
+ // ============================================================================
266
+ // Restore Operations Tests
267
+ // ============================================================================
268
+ (0, vitest_1.describe)("EkoDBClient restore operations", () => {
269
+ (0, vitest_1.it)("restores deleted record", async () => {
270
+ const client = createTestClient();
271
+ mockTokenResponse();
272
+ mockJsonResponse({ status: "restored" });
273
+ await (0, vitest_1.expect)(client.restoreRecord("users", "record_123")).resolves.not.toThrow();
274
+ });
275
+ // Note: restoreCollection return type may vary - covered by integration tests
276
+ });
277
+ // ============================================================================
278
+ // Search Tests
279
+ // ============================================================================
280
+ (0, vitest_1.describe)("EkoDBClient search", () => {
281
+ (0, vitest_1.it)("performs search", async () => {
282
+ const client = createTestClient();
283
+ mockTokenResponse();
284
+ mockJsonResponse({
285
+ results: [
286
+ { id: "doc_1", score: 0.95 },
287
+ { id: "doc_2", score: 0.85 },
288
+ ],
289
+ total: 2,
290
+ });
291
+ const result = await client.search("documents", { query: "test" });
292
+ (0, vitest_1.expect)(result.results).toHaveLength(2);
293
+ (0, vitest_1.expect)(result.total).toBe(2);
294
+ });
295
+ // Note: textSearch and hybridSearch require specific mock setup - covered by integration tests
296
+ });
297
+ // ============================================================================
298
+ // Functions/Scripts Tests
299
+ // ============================================================================
300
+ (0, vitest_1.describe)("EkoDBClient functions", () => {
301
+ (0, vitest_1.it)("calls script", async () => {
302
+ const client = createTestClient();
303
+ mockTokenResponse();
304
+ mockJsonResponse({
305
+ results: [{ id: "user_1", name: "Alice" }],
306
+ });
307
+ const result = await client.callScript("my_function", { limit: 10 });
308
+ (0, vitest_1.expect)(result).toHaveProperty("results");
309
+ });
310
+ (0, vitest_1.it)("lists scripts", async () => {
311
+ const client = createTestClient();
312
+ mockTokenResponse();
313
+ mockJsonResponse([
314
+ { id: "func_1", label: "function_1" },
315
+ { id: "func_2", label: "function_2" },
316
+ ]);
317
+ const result = await client.listScripts();
318
+ (0, vitest_1.expect)(result).toHaveLength(2);
319
+ });
320
+ (0, vitest_1.it)("deletes script", async () => {
321
+ const client = createTestClient();
322
+ mockTokenResponse();
323
+ mockJsonResponse({ status: "deleted" });
324
+ await (0, vitest_1.expect)(client.deleteScript("func_123")).resolves.not.toThrow();
325
+ });
326
+ });
327
+ // ============================================================================
328
+ // Chat Tests
329
+ // ============================================================================
330
+ (0, vitest_1.describe)("EkoDBClient chat", () => {
331
+ (0, vitest_1.it)("creates chat session", async () => {
332
+ const client = createTestClient();
333
+ mockTokenResponse();
334
+ mockJsonResponse({
335
+ chat_id: "chat_123",
336
+ message_id: "msg_001",
337
+ responses: ["Hello!"],
338
+ });
339
+ const result = await client.createChatSession({
340
+ collections: [{ collection_name: "documents" }],
341
+ llm_provider: "openai",
342
+ llm_model: "gpt-4",
343
+ });
344
+ (0, vitest_1.expect)(result).toHaveProperty("chat_id", "chat_123");
345
+ });
346
+ (0, vitest_1.it)("sends chat message", async () => {
347
+ const client = createTestClient();
348
+ mockTokenResponse();
349
+ mockJsonResponse({
350
+ chat_id: "chat_123",
351
+ message_id: "msg_002",
352
+ responses: ["Here is my response."],
353
+ });
354
+ const request = { message: "What is the answer?" };
355
+ const result = await client.chatMessage("chat_123", request);
356
+ (0, vitest_1.expect)(result).toHaveProperty("message_id");
357
+ });
358
+ (0, vitest_1.it)("lists chat sessions", async () => {
359
+ const client = createTestClient();
360
+ mockTokenResponse();
361
+ mockJsonResponse({
362
+ sessions: [
363
+ { id: "chat_1", created_at: "2024-01-01T00:00:00Z" },
364
+ { id: "chat_2", created_at: "2024-01-02T00:00:00Z" },
365
+ ],
366
+ total: 2,
367
+ });
368
+ const result = await client.listChatSessions();
369
+ (0, vitest_1.expect)(result.sessions).toHaveLength(2);
370
+ });
371
+ (0, vitest_1.it)("gets chat session", async () => {
372
+ const client = createTestClient();
373
+ mockTokenResponse();
374
+ mockJsonResponse({
375
+ id: "chat_123",
376
+ created_at: "2024-01-01T00:00:00Z",
377
+ messages: [],
378
+ });
379
+ const result = await client.getChatSession("chat_123");
380
+ (0, vitest_1.expect)(result).toHaveProperty("id", "chat_123");
381
+ });
382
+ (0, vitest_1.it)("deletes chat session", async () => {
383
+ const client = createTestClient();
384
+ mockTokenResponse();
385
+ mockJsonResponse({ status: "deleted" });
386
+ await (0, vitest_1.expect)(client.deleteChatSession("chat_123")).resolves.not.toThrow();
387
+ });
388
+ });
389
+ // ============================================================================
390
+ // Error Handling Tests
391
+ // ============================================================================
392
+ (0, vitest_1.describe)("EkoDBClient error handling", () => {
393
+ (0, vitest_1.it)("handles server error", async () => {
394
+ const client = createTestClient();
395
+ mockTokenResponse();
396
+ mockErrorResponse(500, "Internal Server Error");
397
+ await (0, vitest_1.expect)(client.insert("users", { name: "Test" })).rejects.toThrow();
398
+ });
399
+ (0, vitest_1.it)("handles rate limit error", async () => {
400
+ const client = createTestClient();
401
+ mockTokenResponse();
402
+ mockFetch.mockResolvedValueOnce({
403
+ ok: false,
404
+ status: 429,
405
+ json: async () => ({ error: "Rate limit exceeded" }),
406
+ text: async () => "Rate limit exceeded",
407
+ headers: new Headers({
408
+ "Retry-After": "60",
409
+ }),
410
+ });
411
+ await (0, vitest_1.expect)(client.insert("users", { name: "Test" })).rejects.toThrow();
412
+ });
413
+ (0, vitest_1.it)("handles authentication error", async () => {
414
+ mockFetch.mockResolvedValueOnce({
415
+ ok: false,
416
+ status: 401,
417
+ json: async () => ({ error: "Invalid API key" }),
418
+ text: async () => "Invalid API key",
419
+ headers: new Headers(),
420
+ });
421
+ const client = createTestClient();
422
+ await (0, vitest_1.expect)(client.insert("users", { name: "Test" })).rejects.toThrow();
423
+ });
424
+ });
425
+ // Note: Rate limit info tests require actual HTTP response headers
426
+ // which are handled differently in the real client vs mocks
427
+ // Covered by integration tests
428
+ // ============================================================================
429
+ // Additional Missing Method Tests - Added Jan 4, 2026
430
+ // ============================================================================
431
+ // Note: findAll/findAllWithLimit use WebSocket, not HTTP - covered by integration tests
432
+ (0, vitest_1.describe)("EkoDBClient batchUpdate", () => {
433
+ (0, vitest_1.it)("batch updates records", async () => {
434
+ const client = createTestClient();
435
+ mockTokenResponse();
436
+ mockJsonResponse({
437
+ successful: ["id_1", "id_2"],
438
+ failed: [],
439
+ });
440
+ const updates = [
441
+ { id: "id_1", data: { name: "Updated 1" } },
442
+ { id: "id_2", data: { name: "Updated 2" } },
443
+ ];
444
+ const result = await client.batchUpdate("users", updates);
445
+ (0, vitest_1.expect)(result.successful).toHaveLength(2);
446
+ });
447
+ });
448
+ (0, vitest_1.describe)("EkoDBClient collection management", () => {
449
+ (0, vitest_1.it)("creates collection", async () => {
450
+ const client = createTestClient();
451
+ mockTokenResponse();
452
+ mockJsonResponse({ status: "created" });
453
+ await (0, vitest_1.expect)(client.createCollection("new_collection", { fields: {} })).resolves.not.toThrow();
454
+ });
455
+ (0, vitest_1.it)("gets collection metadata", async () => {
456
+ const client = createTestClient();
457
+ mockTokenResponse();
458
+ mockJsonResponse({
459
+ collection: { fields: { name: { field_type: "String" } } },
460
+ });
461
+ const result = await client.getCollection("users");
462
+ (0, vitest_1.expect)(result).toBeDefined();
463
+ });
464
+ // Note: getSchema requires specific response format - covered by integration tests
465
+ });
466
+ (0, vitest_1.describe)("EkoDBClient KV advanced", () => {
467
+ (0, vitest_1.it)("queries KV by pattern", async () => {
468
+ const client = createTestClient();
469
+ mockTokenResponse();
470
+ mockJsonResponse([
471
+ { key: "config:app", value: "value1" },
472
+ { key: "config:db", value: "value2" },
473
+ ]);
474
+ const result = await client.kvFind({ pattern: "config:*" });
475
+ (0, vitest_1.expect)(result).toHaveLength(2);
476
+ });
477
+ });
478
+ (0, vitest_1.describe)("EkoDBClient scripts advanced", () => {
479
+ (0, vitest_1.it)("saves script", async () => {
480
+ const client = createTestClient();
481
+ mockTokenResponse();
482
+ mockJsonResponse({ id: "func_123", label: "my_function" });
483
+ const script = {
484
+ label: "my_function",
485
+ name: "my_function",
486
+ parameters: {},
487
+ functions: [],
488
+ };
489
+ const result = await client.saveScript(script);
490
+ (0, vitest_1.expect)(result).toBeDefined();
491
+ });
492
+ (0, vitest_1.it)("gets script by ID", async () => {
493
+ const client = createTestClient();
494
+ mockTokenResponse();
495
+ mockJsonResponse({ id: "func_123", label: "my_function" });
496
+ const result = await client.getScript("func_123");
497
+ (0, vitest_1.expect)(result).toBeDefined();
498
+ });
499
+ (0, vitest_1.it)("updates script", async () => {
500
+ const client = createTestClient();
501
+ mockTokenResponse();
502
+ mockJsonResponse({ id: "func_123", label: "updated_function" });
503
+ const script = {
504
+ label: "updated_function",
505
+ name: "updated_function",
506
+ parameters: {},
507
+ functions: [],
508
+ };
509
+ await (0, vitest_1.expect)(client.updateScript("func_123", script)).resolves.not.toThrow();
510
+ });
511
+ });
512
+ (0, vitest_1.describe)("EkoDBClient chat advanced", () => {
513
+ (0, vitest_1.it)("gets chat session messages", async () => {
514
+ const client = createTestClient();
515
+ mockTokenResponse();
516
+ mockJsonResponse({
517
+ messages: [
518
+ { id: "msg_1", role: "user", content: "Hello" },
519
+ { id: "msg_2", role: "assistant", content: "Hi" },
520
+ ],
521
+ });
522
+ const result = await client.getChatSessionMessages("chat_123");
523
+ (0, vitest_1.expect)(result).toBeDefined();
524
+ });
525
+ (0, vitest_1.it)("updates chat session", async () => {
526
+ const client = createTestClient();
527
+ mockTokenResponse();
528
+ mockJsonResponse({ id: "chat_123", updated: true });
529
+ await (0, vitest_1.expect)(client.updateChatSession("chat_123", { system_prompt: "New prompt" })).resolves.not.toThrow();
530
+ });
531
+ (0, vitest_1.it)("branches chat session", async () => {
532
+ const client = createTestClient();
533
+ mockTokenResponse();
534
+ mockJsonResponse({ chat_id: "chat_456", branched_from: "chat_123" });
535
+ const result = await client.branchChatSession({
536
+ collections: [{ collection_name: "documents" }],
537
+ llm_provider: "openai",
538
+ });
539
+ (0, vitest_1.expect)(result).toBeDefined();
540
+ });
541
+ (0, vitest_1.it)("merges chat sessions", async () => {
542
+ const client = createTestClient();
543
+ mockTokenResponse();
544
+ mockJsonResponse({ chat_id: "chat_123", merged: true });
545
+ const result = await client.mergeChatSessions({
546
+ source_chat_ids: ["chat_456"],
547
+ target_chat_id: "chat_123",
548
+ merge_strategy: "Interleaved",
549
+ });
550
+ (0, vitest_1.expect)(result).toBeDefined();
551
+ });
552
+ (0, vitest_1.it)("deletes chat message", async () => {
553
+ const client = createTestClient();
554
+ mockTokenResponse();
555
+ mockJsonResponse({ deleted: true });
556
+ await (0, vitest_1.expect)(client.deleteChatMessage("chat_123", "msg_001")).resolves.not.toThrow();
557
+ });
558
+ (0, vitest_1.it)("updates chat message", async () => {
559
+ const client = createTestClient();
560
+ mockTokenResponse();
561
+ mockJsonResponse({ message_id: "msg_001", updated: true });
562
+ await (0, vitest_1.expect)(client.updateChatMessage("chat_123", "msg_001", "Updated content")).resolves.not.toThrow();
563
+ });
564
+ (0, vitest_1.it)("regenerates chat message", async () => {
565
+ const client = createTestClient();
566
+ mockTokenResponse();
567
+ mockJsonResponse({ message_id: "msg_002", content: "Regenerated" });
568
+ const result = await client.regenerateMessage("chat_123", "msg_001");
569
+ (0, vitest_1.expect)(result).toBeDefined();
570
+ });
571
+ (0, vitest_1.it)("toggles forgotten message", async () => {
572
+ const client = createTestClient();
573
+ mockTokenResponse();
574
+ mockJsonResponse({ message_id: "msg_001", forgotten: true });
575
+ await (0, vitest_1.expect)(client.toggleForgottenMessage("chat_123", "msg_001", true)).resolves.not.toThrow();
576
+ });
577
+ });
578
+ (0, vitest_1.describe)("EkoDBClient transaction status", () => {
579
+ (0, vitest_1.it)("gets transaction status", async () => {
580
+ const client = createTestClient();
581
+ mockTokenResponse();
582
+ mockJsonResponse({ transaction_id: "tx_123", status: "active" });
583
+ const result = await client.getTransactionStatus("tx_123");
584
+ (0, vitest_1.expect)(result).toBeDefined();
585
+ });
586
+ });
587
+ // ============================================================================
588
+ // Convenience Methods Tests
589
+ // ============================================================================
590
+ (0, vitest_1.describe)("Convenience methods", () => {
591
+ (0, vitest_1.describe)("upsert", () => {
592
+ (0, vitest_1.it)("inserts when record not found", async () => {
593
+ mockTokenResponse();
594
+ // Mock update returning 404
595
+ mockErrorResponse(404, "Not found");
596
+ // Mock insert succeeding
597
+ mockJsonResponse({ id: "user123", name: "John Doe" });
598
+ const client = createTestClient();
599
+ await client.init();
600
+ const result = await client.upsert("users", "user123", {
601
+ name: "John Doe",
602
+ });
603
+ (0, vitest_1.expect)(result).toEqual({ id: "user123", name: "John Doe" });
604
+ });
605
+ (0, vitest_1.it)("updates when record exists", async () => {
606
+ mockTokenResponse();
607
+ // Mock update succeeding
608
+ mockJsonResponse({ id: "user123", name: "John Doe Updated" });
609
+ const client = createTestClient();
610
+ await client.init();
611
+ const result = await client.upsert("users", "user123", {
612
+ name: "John Doe Updated",
613
+ });
614
+ (0, vitest_1.expect)(result).toEqual({ id: "user123", name: "John Doe Updated" });
615
+ });
616
+ (0, vitest_1.it)("throws on non-404 errors", async () => {
617
+ mockTokenResponse();
618
+ // Mock update with server error
619
+ mockErrorResponse(500, "Internal server error");
620
+ const client = createTestClient();
621
+ await client.init();
622
+ await (0, vitest_1.expect)(client.upsert("users", "user123", { name: "John Doe" })).rejects.toThrow();
623
+ });
624
+ });
625
+ (0, vitest_1.describe)("findOne", () => {
626
+ (0, vitest_1.it)("returns record when found", async () => {
627
+ mockTokenResponse();
628
+ mockJsonResponse([
629
+ { id: "user123", email: "test@example.com", name: "John" },
630
+ ]);
631
+ const client = createTestClient();
632
+ await client.init();
633
+ const result = await client.findOne("users", "email", "test@example.com");
634
+ (0, vitest_1.expect)(result).toEqual({
635
+ id: "user123",
636
+ email: "test@example.com",
637
+ name: "John",
638
+ });
639
+ });
640
+ (0, vitest_1.it)("returns null when not found", async () => {
641
+ mockTokenResponse();
642
+ mockJsonResponse([]);
643
+ const client = createTestClient();
644
+ await client.init();
645
+ const result = await client.findOne("users", "email", "notfound@example.com");
646
+ (0, vitest_1.expect)(result).toBeNull();
647
+ });
648
+ });
649
+ (0, vitest_1.describe)("exists", () => {
650
+ (0, vitest_1.it)("returns true when record exists", async () => {
651
+ mockTokenResponse();
652
+ mockJsonResponse({ id: "user123", name: "John" });
653
+ const client = createTestClient();
654
+ await client.init();
655
+ const result = await client.exists("users", "user123");
656
+ (0, vitest_1.expect)(result).toBe(true);
657
+ });
658
+ (0, vitest_1.it)("returns false when record not found", async () => {
659
+ mockTokenResponse();
660
+ mockErrorResponse(404, "Not found");
661
+ const client = createTestClient();
662
+ await client.init();
663
+ const result = await client.exists("users", "user123");
664
+ (0, vitest_1.expect)(result).toBe(false);
665
+ });
666
+ (0, vitest_1.it)("throws on non-404 errors", async () => {
667
+ mockTokenResponse();
668
+ mockErrorResponse(500, "Internal server error");
669
+ const client = createTestClient();
670
+ await client.init();
671
+ await (0, vitest_1.expect)(client.exists("users", "user123")).rejects.toThrow();
672
+ });
673
+ });
674
+ (0, vitest_1.describe)("paginate", () => {
675
+ (0, vitest_1.it)("calculates skip correctly for page 2", async () => {
676
+ mockTokenResponse();
677
+ mockJsonResponse([{ id: "user11", name: "User 11" }]);
678
+ const client = createTestClient();
679
+ await client.init();
680
+ const result = await client.paginate("users", 2, 10);
681
+ (0, vitest_1.expect)(result).toHaveLength(1);
682
+ (0, vitest_1.expect)(result[0]).toEqual({ id: "user11", name: "User 11" });
683
+ });
684
+ (0, vitest_1.it)("skips zero records for page 1", async () => {
685
+ mockTokenResponse();
686
+ mockJsonResponse([{ id: "user1", name: "User 1" }]);
687
+ const client = createTestClient();
688
+ await client.init();
689
+ const result = await client.paginate("users", 1, 10);
690
+ (0, vitest_1.expect)(result).toHaveLength(1);
691
+ (0, vitest_1.expect)(result[0]).toEqual({ id: "user1", name: "User 1" });
692
+ });
693
+ (0, vitest_1.it)("returns empty array when no records", async () => {
694
+ mockTokenResponse();
695
+ mockJsonResponse([]);
696
+ const client = createTestClient();
697
+ await client.init();
698
+ const result = await client.paginate("users", 5, 10);
699
+ (0, vitest_1.expect)(result).toHaveLength(0);
700
+ });
701
+ });
702
+ });