@lobehub/lobehub 2.0.0-next.40 → 2.0.0-next.41

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,716 @@
1
+ import { eq } from 'drizzle-orm';
2
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
3
+
4
+ import { uuid } from '@/utils/uuid';
5
+
6
+ import {
7
+ agents,
8
+ chatGroups,
9
+ chunks,
10
+ embeddings,
11
+ fileChunks,
12
+ files,
13
+ messagePlugins,
14
+ messageQueries,
15
+ messageQueryChunks,
16
+ messageTTS,
17
+ messageTranslates,
18
+ messages,
19
+ messagesFiles,
20
+ sessions,
21
+ topics,
22
+ users,
23
+ } from '../../../schemas';
24
+ import { LobeChatDatabase } from '../../../type';
25
+ import { MessageModel } from '../../message';
26
+ import { getTestDB } from '../_util';
27
+ import { codeEmbedding } from '../fixtures/embedding';
28
+
29
+ const serverDB: LobeChatDatabase = await getTestDB();
30
+
31
+ const userId = 'message-update-test';
32
+ const otherUserId = 'message-update-test-other';
33
+ const messageModel = new MessageModel(serverDB, userId);
34
+ const embeddingsId = uuid();
35
+
36
+ beforeEach(async () => {
37
+ // Clear tables before each test case
38
+ await serverDB.transaction(async (trx) => {
39
+ await trx.delete(users).where(eq(users.id, userId));
40
+ await trx.delete(users).where(eq(users.id, otherUserId));
41
+ await trx.insert(users).values([{ id: userId }, { id: otherUserId }]);
42
+
43
+ await trx.insert(sessions).values([
44
+ // { id: 'session1', userId },
45
+ // { id: 'session2', userId },
46
+ { id: '1', userId },
47
+ ]);
48
+ await trx.insert(files).values({
49
+ id: 'f1',
50
+ userId: userId,
51
+ url: 'abc',
52
+ name: 'file-1',
53
+ fileType: 'image/png',
54
+ size: 1000,
55
+ });
56
+
57
+ await trx.insert(embeddings).values({
58
+ id: embeddingsId,
59
+ embeddings: codeEmbedding,
60
+ userId,
61
+ });
62
+ });
63
+ });
64
+
65
+ afterEach(async () => {
66
+ // Clear tables after each test case
67
+ await serverDB.delete(users).where(eq(users.id, userId));
68
+ await serverDB.delete(users).where(eq(users.id, otherUserId));
69
+ });
70
+
71
+ describe('MessageModel Update Tests', () => {
72
+ describe('updateMessage', () => {
73
+ it('should update message content', async () => {
74
+ // Create test data
75
+ await serverDB
76
+ .insert(messages)
77
+ .values([{ id: '1', userId, role: 'user', content: 'message 1' }]);
78
+
79
+ // Call updateMessage method
80
+ await messageModel.update('1', { content: 'updated message' });
81
+
82
+ // Assert result
83
+ const result = await serverDB.select().from(messages).where(eq(messages.id, '1'));
84
+ expect(result[0].content).toBe('updated message');
85
+ });
86
+
87
+ it('should only update messages belonging to the user', async () => {
88
+ // Create test data
89
+ await serverDB
90
+ .insert(messages)
91
+ .values([{ id: '1', userId: otherUserId, role: 'user', content: 'message 1' }]);
92
+
93
+ // Call updateMessage method
94
+ await messageModel.update('1', { content: 'updated message' });
95
+
96
+ // Assert result
97
+ const result = await serverDB.select().from(messages).where(eq(messages.id, '1'));
98
+ expect(result[0].content).toBe('message 1');
99
+ });
100
+
101
+ it('should update message tools', async () => {
102
+ // Create test data
103
+ await serverDB.insert(messages).values([
104
+ {
105
+ id: '1',
106
+ userId,
107
+ role: 'user',
108
+ content: 'message 1',
109
+ tools: [
110
+ {
111
+ id: 'call_Z8UU8LedZcoJHFGkfqYecjmT',
112
+ type: 'builtin',
113
+ apiName: 'searchWithSearXNG',
114
+ arguments:
115
+ '{"query":"杭州洪水 2023","searchEngines":["google","bing","baidu","duckduckgo","brave"]}',
116
+ identifier: 'lobe-web-browsing',
117
+ },
118
+ ],
119
+ },
120
+ ]);
121
+
122
+ // Call updateMessage method
123
+ await messageModel.update('1', {
124
+ tools: [
125
+ {
126
+ id: 'call_Z8UU8LedZcoJHFGkfqYecjmT',
127
+ type: 'builtin',
128
+ apiName: 'searchWithSearXNG',
129
+ arguments: '{"query":"2024 杭州暴雨","searchEngines":["duckduckgo","google","brave"]}',
130
+ identifier: 'lobe-web-browsing',
131
+ },
132
+ ],
133
+ });
134
+
135
+ // Assert result
136
+ const result = await serverDB.select().from(messages).where(eq(messages.id, '1'));
137
+ expect((result[0].tools as any)[0].arguments).toBe(
138
+ '{"query":"2024 杭州暴雨","searchEngines":["duckduckgo","google","brave"]}',
139
+ );
140
+ });
141
+
142
+ describe('update with imageList', () => {
143
+ it('should update a message and add image files', async () => {
144
+ // Create test data
145
+ await serverDB.insert(messages).values({
146
+ id: 'msg-to-update',
147
+ userId,
148
+ role: 'user',
149
+ content: 'original content',
150
+ });
151
+
152
+ await serverDB.insert(files).values([
153
+ {
154
+ id: 'img1',
155
+ name: 'image1.jpg',
156
+ fileType: 'image/jpeg',
157
+ size: 100,
158
+ url: 'url1',
159
+ userId,
160
+ },
161
+ { id: 'img2', name: 'image2.png', fileType: 'image/png', size: 200, url: 'url2', userId },
162
+ ]);
163
+
164
+ // Call update method
165
+ await messageModel.update('msg-to-update', {
166
+ content: 'updated content',
167
+ imageList: [
168
+ { id: 'img1', alt: 'image 1', url: 'url1' },
169
+ { id: 'img2', alt: 'image 2', url: 'url2' },
170
+ ],
171
+ });
172
+
173
+ // Verify message updated successfully
174
+ const updatedMessage = await serverDB
175
+ .select()
176
+ .from(messages)
177
+ .where(eq(messages.id, 'msg-to-update'));
178
+
179
+ expect(updatedMessage[0].content).toBe('updated content');
180
+
181
+ // Verify message file associations created successfully
182
+ const messageFiles = await serverDB
183
+ .select()
184
+ .from(messagesFiles)
185
+ .where(eq(messagesFiles.messageId, 'msg-to-update'));
186
+
187
+ expect(messageFiles).toHaveLength(2);
188
+ expect(messageFiles[0].fileId).toBe('img1');
189
+ expect(messageFiles[1].fileId).toBe('img2');
190
+ });
191
+
192
+ it('should handle empty imageList', async () => {
193
+ // Create test data
194
+ await serverDB.insert(messages).values({
195
+ id: 'msg-no-images',
196
+ userId,
197
+ role: 'user',
198
+ content: 'original content',
199
+ });
200
+
201
+ // Call update method without providing imageList
202
+ await messageModel.update('msg-no-images', {
203
+ content: 'updated content',
204
+ });
205
+
206
+ // Verify message updated successfully
207
+ const updatedMessage = await serverDB
208
+ .select()
209
+ .from(messages)
210
+ .where(eq(messages.id, 'msg-no-images'));
211
+
212
+ expect(updatedMessage[0].content).toBe('updated content');
213
+
214
+ // Verify no message file associations created
215
+ const messageFiles = await serverDB
216
+ .select()
217
+ .from(messagesFiles)
218
+ .where(eq(messagesFiles.messageId, 'msg-no-images'));
219
+
220
+ expect(messageFiles).toHaveLength(0);
221
+ });
222
+
223
+ it('should update multiple fields at once', async () => {
224
+ // Create test data
225
+ await serverDB.insert(messages).values({
226
+ id: 'msg-multi-update',
227
+ userId,
228
+ role: 'user',
229
+ content: 'original content',
230
+ model: 'gpt-3.5',
231
+ });
232
+
233
+ // Call update method to update multiple fields
234
+ await messageModel.update('msg-multi-update', {
235
+ content: 'updated content',
236
+ role: 'assistant',
237
+ model: 'gpt-4',
238
+ metadata: { tps: 1 },
239
+ });
240
+
241
+ // Verify message updated successfully
242
+ const updatedMessage = await serverDB
243
+ .select()
244
+ .from(messages)
245
+ .where(eq(messages.id, 'msg-multi-update'));
246
+
247
+ expect(updatedMessage[0].content).toBe('updated content');
248
+ expect(updatedMessage[0].role).toBe('assistant');
249
+ expect(updatedMessage[0].model).toBe('gpt-4');
250
+ expect(updatedMessage[0].metadata).toEqual({ tps: 1 });
251
+ });
252
+ });
253
+ });
254
+
255
+ describe('deleteMessage', () => {
256
+ it('should delete a message', async () => {
257
+ // Create test data
258
+ await serverDB
259
+ .insert(messages)
260
+ .values([{ id: '1', userId, role: 'user', content: 'message 1' }]);
261
+
262
+ // 调用 deleteMessage 方法
263
+ await messageModel.deleteMessage('1');
264
+
265
+ // Assert result
266
+ const result = await serverDB.select().from(messages).where(eq(messages.id, '1'));
267
+ expect(result).toHaveLength(0);
268
+ });
269
+
270
+ it('should delete a message with tool calls', async () => {
271
+ // Create test data
272
+ await serverDB.transaction(async (trx) => {
273
+ await trx.insert(messages).values([
274
+ { id: '1', userId, role: 'user', content: 'message 1', tools: [{ id: 'tool1' }] },
275
+ { id: '2', userId, role: 'tool', content: 'message 1' },
276
+ ]);
277
+ await trx
278
+ .insert(messagePlugins)
279
+ .values([{ id: '2', toolCallId: 'tool1', identifier: 'plugin-1', userId }]);
280
+ });
281
+
282
+ // 调用 deleteMessage 方法
283
+ await messageModel.deleteMessage('1');
284
+
285
+ // Assert result
286
+ const result = await serverDB.select().from(messages).where(eq(messages.id, '1'));
287
+ expect(result).toHaveLength(0);
288
+
289
+ const result2 = await serverDB
290
+ .select()
291
+ .from(messagePlugins)
292
+ .where(eq(messagePlugins.id, '2'));
293
+
294
+ expect(result2).toHaveLength(0);
295
+ });
296
+
297
+ it('should only delete messages belonging to the user', async () => {
298
+ // Create test data
299
+ await serverDB
300
+ .insert(messages)
301
+ .values([{ id: '1', userId: otherUserId, role: 'user', content: 'message 1' }]);
302
+
303
+ // 调用 deleteMessage 方法
304
+ await messageModel.deleteMessage('1');
305
+
306
+ // Assert result
307
+ const result = await serverDB.select().from(messages).where(eq(messages.id, '1'));
308
+ expect(result).toHaveLength(1);
309
+ });
310
+ });
311
+
312
+ describe('deleteMessages', () => {
313
+ it('should delete 2 messages', async () => {
314
+ // Create test data
315
+ await serverDB.insert(messages).values([
316
+ { id: '1', userId, role: 'user', content: 'message 1' },
317
+ { id: '2', userId, role: 'user', content: 'message 2' },
318
+ ]);
319
+
320
+ // 调用 deleteMessage 方法
321
+ await messageModel.deleteMessages(['1', '2']);
322
+
323
+ // Assert result
324
+ const result = await serverDB.select().from(messages).where(eq(messages.id, '1'));
325
+ expect(result).toHaveLength(0);
326
+ const result2 = await serverDB.select().from(messages).where(eq(messages.id, '2'));
327
+ expect(result2).toHaveLength(0);
328
+ });
329
+
330
+ it('should only delete messages belonging to the user', async () => {
331
+ // Create test data
332
+ await serverDB.insert(messages).values([
333
+ { id: '1', userId: otherUserId, role: 'user', content: 'message 1' },
334
+ { id: '2', userId: otherUserId, role: 'user', content: 'message 1' },
335
+ ]);
336
+
337
+ // 调用 deleteMessage 方法
338
+ await messageModel.deleteMessages(['1', '2']);
339
+
340
+ // Assert result
341
+ const result = await serverDB.select().from(messages).where(eq(messages.id, '1'));
342
+ expect(result).toHaveLength(1);
343
+ });
344
+ });
345
+
346
+ describe('deleteAllMessages', () => {
347
+ it('should delete all messages belonging to the user', async () => {
348
+ // Create test data
349
+ await serverDB.insert(messages).values([
350
+ { id: '1', userId, role: 'user', content: 'message 1' },
351
+ { id: '2', userId, role: 'user', content: 'message 2' },
352
+ { id: '3', userId: otherUserId, role: 'user', content: 'message 3' },
353
+ ]);
354
+
355
+ // 调用 deleteAllMessages 方法
356
+ await messageModel.deleteAllMessages();
357
+
358
+ // Assert result
359
+ const result = await serverDB.select().from(messages).where(eq(messages.userId, userId));
360
+
361
+ expect(result).toHaveLength(0);
362
+
363
+ const otherResult = await serverDB
364
+ .select()
365
+ .from(messages)
366
+ .where(eq(messages.userId, otherUserId));
367
+
368
+ expect(otherResult).toHaveLength(1);
369
+ });
370
+ });
371
+
372
+ describe('updatePluginState', () => {
373
+ it('should update the state field in messagePlugins table', async () => {
374
+ // Create test data
375
+ await serverDB.insert(messages).values({ id: '1', content: 'abc', role: 'user', userId });
376
+ await serverDB.insert(messagePlugins).values([
377
+ {
378
+ id: '1',
379
+ toolCallId: 'tool1',
380
+ identifier: 'plugin1',
381
+ state: { key1: 'value1' },
382
+ userId,
383
+ },
384
+ ]);
385
+
386
+ // 调用 updatePluginState 方法
387
+ await messageModel.updatePluginState('1', { key2: 'value2' });
388
+
389
+ // Assert result
390
+ const result = await serverDB.select().from(messagePlugins).where(eq(messagePlugins.id, '1'));
391
+
392
+ expect(result[0].state).toEqual({ key1: 'value1', key2: 'value2' });
393
+ });
394
+
395
+ it('should throw an error if plugin does not exist', async () => {
396
+ // 调用 updatePluginState 方法
397
+ await expect(messageModel.updatePluginState('1', { key: 'value' })).rejects.toThrowError(
398
+ 'Plugin not found',
399
+ );
400
+ });
401
+ });
402
+ describe('updateMessagePlugin', () => {
403
+ it('should update the state field in messagePlugins table', async () => {
404
+ // Create test data
405
+ await serverDB.insert(messages).values({ id: '1', content: 'abc', role: 'user', userId });
406
+ await serverDB.insert(messagePlugins).values([
407
+ {
408
+ id: '1',
409
+ toolCallId: 'tool1',
410
+ identifier: 'plugin1',
411
+ state: { key1: 'value1' },
412
+ userId,
413
+ },
414
+ ]);
415
+
416
+ // 调用 updatePluginState 方法
417
+ await messageModel.updateMessagePlugin('1', { identifier: 'plugin2' });
418
+
419
+ // Assert result
420
+ const result = await serverDB.select().from(messagePlugins).where(eq(messagePlugins.id, '1'));
421
+
422
+ expect(result[0].identifier).toEqual('plugin2');
423
+ });
424
+
425
+ it('should throw an error if plugin does not exist', async () => {
426
+ // 调用 updatePluginState 方法
427
+ await expect(messageModel.updatePluginState('1', { key: 'value' })).rejects.toThrowError(
428
+ 'Plugin not found',
429
+ );
430
+ });
431
+ });
432
+
433
+ describe('updateMetadata', () => {
434
+ it('should update metadata for an existing message', async () => {
435
+ // Create test data
436
+ await serverDB.insert(messages).values({
437
+ id: 'msg-with-metadata',
438
+ userId,
439
+ role: 'user',
440
+ content: 'test message',
441
+ metadata: { existingKey: 'existingValue' },
442
+ });
443
+
444
+ // 调用 updateMetadata 方法
445
+ await messageModel.updateMetadata('msg-with-metadata', { newKey: 'newValue' });
446
+
447
+ // Assert result
448
+ const result = await serverDB
449
+ .select()
450
+ .from(messages)
451
+ .where(eq(messages.id, 'msg-with-metadata'));
452
+
453
+ expect(result[0].metadata).toEqual({
454
+ existingKey: 'existingValue',
455
+ newKey: 'newValue',
456
+ });
457
+ });
458
+
459
+ it('should merge new metadata with existing metadata using lodash merge behavior', async () => {
460
+ // Create test data
461
+ await serverDB.insert(messages).values({
462
+ id: 'msg-merge-metadata',
463
+ userId,
464
+ role: 'assistant',
465
+ content: 'test message',
466
+ metadata: {
467
+ level1: {
468
+ level2a: 'original',
469
+ level2b: { level3: 'deep' },
470
+ },
471
+ array: [1, 2, 3],
472
+ },
473
+ });
474
+
475
+ // 调用 updateMetadata 方法
476
+ await messageModel.updateMetadata('msg-merge-metadata', {
477
+ level1: {
478
+ level2a: 'updated',
479
+ level2c: 'new',
480
+ },
481
+ newTopLevel: 'value',
482
+ });
483
+
484
+ // Assert result - 应该使用 lodash merge 行为
485
+ const result = await serverDB
486
+ .select()
487
+ .from(messages)
488
+ .where(eq(messages.id, 'msg-merge-metadata'));
489
+
490
+ expect(result[0].metadata).toEqual({
491
+ level1: {
492
+ level2a: 'updated',
493
+ level2b: { level3: 'deep' },
494
+ level2c: 'new',
495
+ },
496
+ array: [1, 2, 3],
497
+ newTopLevel: 'value',
498
+ });
499
+ });
500
+
501
+ it('should handle non-existent message IDs', async () => {
502
+ // 调用 updateMetadata 方法,尝试更新不存在的消息
503
+ const result = await messageModel.updateMetadata('non-existent-id', { key: 'value' });
504
+
505
+ // Assert result - 应该返回 undefined
506
+ expect(result).toBeUndefined();
507
+ });
508
+
509
+ it('should handle empty metadata updates', async () => {
510
+ // Create test data
511
+ await serverDB.insert(messages).values({
512
+ id: 'msg-empty-metadata',
513
+ userId,
514
+ role: 'user',
515
+ content: 'test message',
516
+ metadata: { originalKey: 'originalValue' },
517
+ });
518
+
519
+ // 调用 updateMetadata 方法,传递空对象
520
+ await messageModel.updateMetadata('msg-empty-metadata', {});
521
+
522
+ // Assert result - 原始 metadata 应该保持不变
523
+ const result = await serverDB
524
+ .select()
525
+ .from(messages)
526
+ .where(eq(messages.id, 'msg-empty-metadata'));
527
+
528
+ expect(result[0].metadata).toEqual({ originalKey: 'originalValue' });
529
+ });
530
+
531
+ it('should handle message with null metadata', async () => {
532
+ // Create test data
533
+ await serverDB.insert(messages).values({
534
+ id: 'msg-null-metadata',
535
+ userId,
536
+ role: 'user',
537
+ content: 'test message',
538
+ metadata: null,
539
+ });
540
+
541
+ // 调用 updateMetadata 方法
542
+ await messageModel.updateMetadata('msg-null-metadata', { key: 'value' });
543
+
544
+ // Assert result - 应该创建新的 metadata
545
+ const result = await serverDB
546
+ .select()
547
+ .from(messages)
548
+ .where(eq(messages.id, 'msg-null-metadata'));
549
+
550
+ expect(result[0].metadata).toEqual({ key: 'value' });
551
+ });
552
+
553
+ it('should only update messages belonging to the current user', async () => {
554
+ // Create test data - 其他用户的消息
555
+ await serverDB.insert(messages).values({
556
+ id: 'msg-other-user',
557
+ userId: otherUserId,
558
+ role: 'user',
559
+ content: 'test message',
560
+ metadata: { originalKey: 'originalValue' },
561
+ });
562
+
563
+ // 调用 updateMetadata 方法
564
+ const result = await messageModel.updateMetadata('msg-other-user', {
565
+ hackedKey: 'hackedValue',
566
+ });
567
+
568
+ // Assert result - 应该返回 undefined
569
+ expect(result).toBeUndefined();
570
+
571
+ // 验证原始 metadata 未被修改
572
+ const dbResult = await serverDB
573
+ .select()
574
+ .from(messages)
575
+ .where(eq(messages.id, 'msg-other-user'));
576
+
577
+ expect(dbResult[0].metadata).toEqual({ originalKey: 'originalValue' });
578
+ });
579
+
580
+ it('should handle complex nested metadata updates', async () => {
581
+ // Create test data
582
+ await serverDB.insert(messages).values({
583
+ id: 'msg-complex-metadata',
584
+ userId,
585
+ role: 'assistant',
586
+ content: 'test message',
587
+ metadata: {
588
+ config: {
589
+ settings: {
590
+ enabled: true,
591
+ options: ['a', 'b'],
592
+ },
593
+ version: 1,
594
+ },
595
+ },
596
+ });
597
+
598
+ // 调用 updateMetadata 方法
599
+ await messageModel.updateMetadata('msg-complex-metadata', {
600
+ config: {
601
+ settings: {
602
+ enabled: false,
603
+ timeout: 5000,
604
+ },
605
+ newField: 'value',
606
+ },
607
+ stats: { count: 10 },
608
+ });
609
+
610
+ // Assert result
611
+ const result = await serverDB
612
+ .select()
613
+ .from(messages)
614
+ .where(eq(messages.id, 'msg-complex-metadata'));
615
+
616
+ expect(result[0].metadata).toEqual({
617
+ config: {
618
+ settings: {
619
+ enabled: false,
620
+ options: ['a', 'b'],
621
+ timeout: 5000,
622
+ },
623
+ version: 1,
624
+ newField: 'value',
625
+ },
626
+ stats: { count: 10 },
627
+ });
628
+ });
629
+ });
630
+
631
+ describe('updateTranslate', () => {
632
+ it('should insert a new record if message does not exist in messageTranslates table', async () => {
633
+ // Create test data
634
+ await serverDB
635
+ .insert(messages)
636
+ .values([{ id: '1', userId, role: 'user', content: 'message 1' }]);
637
+
638
+ // 调用 updateTranslate 方法
639
+ await messageModel.updateTranslate('1', {
640
+ content: 'translated message 1',
641
+ from: 'en',
642
+ to: 'zh',
643
+ });
644
+
645
+ // Assert result
646
+ const result = await serverDB
647
+ .select()
648
+ .from(messageTranslates)
649
+ .where(eq(messageTranslates.id, '1'));
650
+
651
+ expect(result).toHaveLength(1);
652
+ expect(result[0].content).toBe('translated message 1');
653
+ });
654
+
655
+ it('should update the corresponding fields if message exists in messageTranslates table', async () => {
656
+ // Create test data
657
+ await serverDB.transaction(async (trx) => {
658
+ await trx
659
+ .insert(messages)
660
+ .values([{ id: '1', userId, role: 'user', content: 'message 1' }]);
661
+ await trx
662
+ .insert(messageTranslates)
663
+ .values([{ id: '1', content: 'translated message 1', from: 'en', to: 'zh', userId }]);
664
+ });
665
+
666
+ // 调用 updateTranslate 方法
667
+ await messageModel.updateTranslate('1', { content: 'updated translated message 1' });
668
+
669
+ // Assert result
670
+ const result = await serverDB
671
+ .select()
672
+ .from(messageTranslates)
673
+ .where(eq(messageTranslates.id, '1'));
674
+
675
+ expect(result[0].content).toBe('updated translated message 1');
676
+ });
677
+ });
678
+
679
+ describe('updateTTS', () => {
680
+ it('should insert a new record if message does not exist in messageTTS table', async () => {
681
+ // Create test data
682
+ await serverDB
683
+ .insert(messages)
684
+ .values([{ id: '1', userId, role: 'user', content: 'message 1' }]);
685
+
686
+ // 调用 updateTTS 方法
687
+ await messageModel.updateTTS('1', { contentMd5: 'md5', file: 'f1', voice: 'voice1' });
688
+
689
+ // Assert result
690
+ const result = await serverDB.select().from(messageTTS).where(eq(messageTTS.id, '1'));
691
+
692
+ expect(result).toHaveLength(1);
693
+ expect(result[0].voice).toBe('voice1');
694
+ });
695
+
696
+ it('should update the corresponding fields if message exists in messageTTS table', async () => {
697
+ // Create test data
698
+ await serverDB.transaction(async (trx) => {
699
+ await trx
700
+ .insert(messages)
701
+ .values([{ id: '1', userId, role: 'user', content: 'message 1' }]);
702
+ await trx
703
+ .insert(messageTTS)
704
+ .values([{ id: '1', contentMd5: 'md5', fileId: 'f1', voice: 'voice1', userId }]);
705
+ });
706
+
707
+ // 调用 updateTTS 方法
708
+ await messageModel.updateTTS('1', { voice: 'updated voice1' });
709
+
710
+ // Assert result
711
+ const result = await serverDB.select().from(messageTTS).where(eq(messageTTS.id, '1'));
712
+
713
+ expect(result[0].voice).toBe('updated voice1');
714
+ });
715
+ });
716
+ });