@lobehub/lobehub 2.0.0-next.29 → 2.0.0-next.30

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.
@@ -101,7 +101,7 @@ describe('Message Router Integration Tests', () => {
101
101
 
102
102
  // 先创建 thread
103
103
  const { threads } = await import('@/database/schemas');
104
- const [thread] = await serverDB
104
+ const [thread] = (await serverDB
105
105
  .insert(threads)
106
106
  .values({
107
107
  userId,
@@ -109,7 +109,7 @@ describe('Message Router Integration Tests', () => {
109
109
  sourceMessageId: 'msg-source',
110
110
  type: 'continuation', // type is required
111
111
  })
112
- .returning();
112
+ .returning()) as any;
113
113
 
114
114
  const result = await caller.createNewMessage({
115
115
  content: 'Test message in thread',
@@ -316,6 +316,74 @@ describe('Message Router Integration Tests', () => {
316
316
  expect(page1Ids).not.toEqual(page2Ids);
317
317
  }
318
318
  });
319
+
320
+ it('should return messages filtered by groupId', async () => {
321
+ const caller = messageRouter.createCaller(createTestContext(userId));
322
+
323
+ // 首先创建一个 chat_group
324
+ const { chatGroups } = await import('@/database/schemas');
325
+ const [chatGroup] = await serverDB
326
+ .insert(chatGroups)
327
+ .values({
328
+ userId,
329
+ title: 'Test Chat Group',
330
+ })
331
+ .returning();
332
+
333
+ // 创建消息并设置 groupId
334
+ const msg1 = await caller.createNewMessage({
335
+ content: 'Message 1 in group',
336
+ role: 'assistant',
337
+ sessionId: testSessionId,
338
+ });
339
+
340
+ await serverDB
341
+ .update(messages)
342
+ .set({ groupId: chatGroup.id })
343
+ .where(eq(messages.id, msg1.id));
344
+
345
+ // 创建不在 group 中的消息
346
+ await caller.createNewMessage({
347
+ content: 'Message without group',
348
+ role: 'user',
349
+ sessionId: testSessionId,
350
+ });
351
+
352
+ // 查询 group 中的消息
353
+ const result = await caller.getMessages({
354
+ sessionId: testSessionId,
355
+ groupId: chatGroup.id,
356
+ });
357
+
358
+ expect(result).toHaveLength(1);
359
+ expect(result[0].id).toBe(msg1.id);
360
+ });
361
+
362
+ it('should support useGroup parameter', async () => {
363
+ const caller = messageRouter.createCaller(createTestContext(userId));
364
+
365
+ // 创建多个消息
366
+ await caller.createNewMessage({
367
+ content: 'Message 1',
368
+ role: 'assistant',
369
+ sessionId: testSessionId,
370
+ });
371
+
372
+ await caller.createNewMessage({
373
+ content: 'Message 2',
374
+ role: 'assistant',
375
+ sessionId: testSessionId,
376
+ });
377
+
378
+ // useGroup 参数应该影响消息分组展示
379
+ const result = await caller.getMessages({
380
+ sessionId: testSessionId,
381
+ useGroup: true,
382
+ });
383
+
384
+ expect(result).toBeDefined();
385
+ expect(Array.isArray(result)).toBe(true);
386
+ });
319
387
  });
320
388
 
321
389
  describe('removeMessages', () => {
@@ -346,6 +414,160 @@ describe('Message Router Integration Tests', () => {
346
414
 
347
415
  expect(remainingMessages).toHaveLength(0);
348
416
  });
417
+
418
+ it('should return message list when sessionId is provided', async () => {
419
+ const caller = messageRouter.createCaller(createTestContext(userId));
420
+
421
+ // 创建消息
422
+ const msg1Result = await caller.createNewMessage({
423
+ content: 'Message 1',
424
+ role: 'user',
425
+ sessionId: testSessionId,
426
+ });
427
+
428
+ const msg2Result = await caller.createNewMessage({
429
+ content: 'Message 2',
430
+ role: 'user',
431
+ sessionId: testSessionId,
432
+ });
433
+
434
+ const msg3Result = await caller.createNewMessage({
435
+ content: 'Message 3',
436
+ role: 'user',
437
+ sessionId: testSessionId,
438
+ });
439
+
440
+ // 删除消息并返回列表
441
+ const result = await caller.removeMessages({
442
+ ids: [msg1Result.id],
443
+ sessionId: testSessionId,
444
+ });
445
+
446
+ expect(result.success).toBe(true);
447
+ expect(result.messages).toBeDefined();
448
+ expect(result.messages).toHaveLength(2);
449
+ expect(result.messages?.map((m) => m.id)).toContain(msg2Result.id);
450
+ expect(result.messages?.map((m) => m.id)).toContain(msg3Result.id);
451
+ });
452
+ });
453
+
454
+ describe('removeMessage', () => {
455
+ it('should remove a single message', async () => {
456
+ const caller = messageRouter.createCaller(createTestContext(userId));
457
+
458
+ const msgResult = await caller.createNewMessage({
459
+ content: 'Message to remove',
460
+ role: 'user',
461
+ sessionId: testSessionId,
462
+ });
463
+
464
+ await caller.removeMessage({ id: msgResult.id });
465
+
466
+ // 验证消息已删除
467
+ const deletedMessage = await serverDB
468
+ .select()
469
+ .from(messages)
470
+ .where(eq(messages.id, msgResult.id));
471
+
472
+ expect(deletedMessage).toHaveLength(0);
473
+ });
474
+
475
+ it('should return message list when sessionId is provided', async () => {
476
+ const caller = messageRouter.createCaller(createTestContext(userId));
477
+
478
+ const msg1Result = await caller.createNewMessage({
479
+ content: 'Message 1',
480
+ role: 'user',
481
+ sessionId: testSessionId,
482
+ });
483
+
484
+ const msg2Result = await caller.createNewMessage({
485
+ content: 'Message 2',
486
+ role: 'user',
487
+ sessionId: testSessionId,
488
+ });
489
+
490
+ const result = await caller.removeMessage({
491
+ id: msg1Result.id,
492
+ sessionId: testSessionId,
493
+ });
494
+
495
+ expect(result.success).toBe(true);
496
+ expect(result.messages).toBeDefined();
497
+ expect(result.messages).toHaveLength(1);
498
+ expect(result.messages?.[0].id).toBe(msg2Result.id);
499
+ });
500
+ });
501
+
502
+ describe('removeAllMessages', () => {
503
+ it('should remove all messages for the user', async () => {
504
+ const caller = messageRouter.createCaller(createTestContext(userId));
505
+
506
+ // 创建多个 session 和消息
507
+ await caller.createNewMessage({
508
+ content: 'Message 1',
509
+ role: 'user',
510
+ sessionId: testSessionId,
511
+ });
512
+
513
+ const [anotherSession] = await serverDB
514
+ .insert(sessions)
515
+ .values({
516
+ userId,
517
+ type: 'agent',
518
+ })
519
+ .returning();
520
+
521
+ await caller.createNewMessage({
522
+ content: 'Message 2',
523
+ role: 'user',
524
+ sessionId: anotherSession.id,
525
+ });
526
+
527
+ // 删除所有消息
528
+ await caller.removeAllMessages();
529
+
530
+ // 验证所有消息已删除
531
+ const remainingMessages = await serverDB
532
+ .select()
533
+ .from(messages)
534
+ .where(eq(messages.userId, userId));
535
+
536
+ expect(remainingMessages).toHaveLength(0);
537
+ });
538
+ });
539
+
540
+ describe('removeMessageQuery', () => {
541
+ it('should remove message query', async () => {
542
+ const caller = messageRouter.createCaller(createTestContext(userId));
543
+
544
+ const msgResult = await caller.createNewMessage({
545
+ content: 'Message with query',
546
+ role: 'user',
547
+ sessionId: testSessionId,
548
+ });
549
+
550
+ // 创建一个 message query 记录,使用 UUID
551
+ const { messageQueries } = await import('@/database/schemas');
552
+ const [queryRecord] = await serverDB
553
+ .insert(messageQueries)
554
+ .values({
555
+ messageId: msgResult.id,
556
+ userId,
557
+ userQuery: 'test query',
558
+ })
559
+ .returning();
560
+
561
+ await caller.removeMessageQuery({ id: queryRecord.id });
562
+
563
+ // 验证消息查询已删除
564
+ const deletedQuery = await serverDB
565
+ .select()
566
+ .from(messageQueries)
567
+ .where(eq(messageQueries.id, queryRecord.id));
568
+
569
+ expect(deletedQuery).toHaveLength(0);
570
+ });
349
571
  });
350
572
 
351
573
  describe('removeMessagesByAssistant', () => {
@@ -414,6 +636,43 @@ describe('Message Router Integration Tests', () => {
414
636
  });
415
637
  });
416
638
 
639
+ describe('removeMessagesByGroup', () => {
640
+ it('should call removeMessagesByGroup endpoint', async () => {
641
+ const caller = messageRouter.createCaller(createTestContext(userId));
642
+
643
+ // 首先创建一个 chat_group
644
+ const { chatGroups } = await import('@/database/schemas');
645
+ const [chatGroup] = await serverDB
646
+ .insert(chatGroups)
647
+ .values({
648
+ userId,
649
+ title: 'Test Chat Group for Delete',
650
+ })
651
+ .returning();
652
+
653
+ // 创建消息并设置 groupId
654
+ const msg1 = await caller.createNewMessage({
655
+ content: 'Message 1 in group',
656
+ role: 'assistant',
657
+ sessionId: testSessionId,
658
+ topicId: testTopicId,
659
+ });
660
+
661
+ await serverDB
662
+ .update(messages)
663
+ .set({ groupId: chatGroup.id })
664
+ .where(eq(messages.id, msg1.id));
665
+
666
+ // 调用删除接口(不会抛出错误即为成功)
667
+ await expect(
668
+ caller.removeMessagesByGroup({
669
+ groupId: chatGroup.id,
670
+ topicId: testTopicId,
671
+ }),
672
+ ).resolves.not.toThrow();
673
+ });
674
+ });
675
+
417
676
  describe('update', () => {
418
677
  it('should update message content', async () => {
419
678
  const caller = messageRouter.createCaller(createTestContext(userId));
@@ -438,6 +697,36 @@ describe('Message Router Integration Tests', () => {
438
697
 
439
698
  expect(updatedMessage.content).toBe('Updated content');
440
699
  });
700
+
701
+ it('should update message and return message list when sessionId is provided', async () => {
702
+ const caller = messageRouter.createCaller(createTestContext(userId));
703
+
704
+ const msg1 = await caller.createNewMessage({
705
+ content: 'Message 1',
706
+ role: 'user',
707
+ sessionId: testSessionId,
708
+ });
709
+
710
+ const msg2 = await caller.createNewMessage({
711
+ content: 'Message 2',
712
+ role: 'user',
713
+ sessionId: testSessionId,
714
+ });
715
+
716
+ const result = await caller.update({
717
+ id: msg1.id,
718
+ sessionId: testSessionId,
719
+ value: {
720
+ content: 'Updated Message 1',
721
+ },
722
+ });
723
+
724
+ expect(result).toBeDefined();
725
+ // The update method returns the updated message list
726
+ const messages = await caller.getMessages({ sessionId: testSessionId });
727
+ expect(messages).toHaveLength(2);
728
+ expect(messages.find((m) => m.id === msg1.id)?.content).toBe('Updated Message 1');
729
+ });
441
730
  });
442
731
 
443
732
  describe('searchMessages', () => {
@@ -465,6 +754,458 @@ describe('Message Router Integration Tests', () => {
465
754
  });
466
755
  });
467
756
 
757
+ describe('updateMessagePlugin', () => {
758
+ it('should update message plugin state', async () => {
759
+ const caller = messageRouter.createCaller(createTestContext(userId));
760
+
761
+ const msg = await caller.createNewMessage({
762
+ content: 'Message with plugin',
763
+ role: 'assistant',
764
+ sessionId: testSessionId,
765
+ });
766
+
767
+ // 先创建一个 plugin 记录
768
+ const { messagePlugins } = await import('@/database/schemas');
769
+ await serverDB.insert(messagePlugins).values({
770
+ id: msg.id,
771
+ userId,
772
+ toolCallId: 'test-tool-call',
773
+ type: 'default',
774
+ });
775
+
776
+ await caller.updateMessagePlugin({
777
+ id: msg.id,
778
+ value: {
779
+ state: { key: 'value' },
780
+ },
781
+ });
782
+
783
+ const [updatedPlugin] = await serverDB
784
+ .select()
785
+ .from(messagePlugins)
786
+ .where(eq(messagePlugins.id, msg.id));
787
+
788
+ expect(updatedPlugin).toBeDefined();
789
+ expect(updatedPlugin.state).toBeDefined();
790
+ });
791
+ });
792
+
793
+ describe('updateMessageRAG', () => {
794
+ it('should update message RAG information', async () => {
795
+ const caller = messageRouter.createCaller(createTestContext(userId));
796
+
797
+ const msg = await caller.createNewMessage({
798
+ content: 'Message with RAG',
799
+ role: 'assistant',
800
+ sessionId: testSessionId,
801
+ });
802
+
803
+ // 创建必要的依赖: chunks -> messageQueries -> messageQueryChunks
804
+ const { chunks, messageQueries, messageQueryChunks } = await import('@/database/schemas');
805
+
806
+ // 1. 创建 chunk
807
+ const [chunk] = await serverDB
808
+ .insert(chunks)
809
+ .values({
810
+ userId,
811
+ text: 'test chunk content',
812
+ })
813
+ .returning();
814
+
815
+ // 2. 创建 message query
816
+ const [query] = await serverDB
817
+ .insert(messageQueries)
818
+ .values({
819
+ messageId: msg.id,
820
+ userId,
821
+ userQuery: 'test query',
822
+ })
823
+ .returning();
824
+
825
+ // 3. 调用 updateMessageRAG
826
+ await caller.updateMessageRAG({
827
+ id: msg.id,
828
+ value: {
829
+ fileChunks: [{ id: chunk.id, similarity: 0.95 }],
830
+ ragQueryId: query.id,
831
+ },
832
+ });
833
+
834
+ // 验证 messageQueryChunks 记录已创建
835
+ const [queryChunk] = await serverDB
836
+ .select()
837
+ .from(messageQueryChunks)
838
+ .where(eq(messageQueryChunks.messageId, msg.id));
839
+
840
+ expect(queryChunk).toBeDefined();
841
+ expect(queryChunk.chunkId).toBe(chunk.id);
842
+ });
843
+
844
+ it('should return message list when sessionId is provided', async () => {
845
+ const caller = messageRouter.createCaller(createTestContext(userId));
846
+
847
+ const msg1 = await caller.createNewMessage({
848
+ content: 'Message 1',
849
+ role: 'assistant',
850
+ sessionId: testSessionId,
851
+ });
852
+
853
+ await caller.createNewMessage({
854
+ content: 'Message 2',
855
+ role: 'assistant',
856
+ sessionId: testSessionId,
857
+ });
858
+
859
+ // 创建必要的依赖: chunks -> messageQueries
860
+ const { chunks, messageQueries } = await import('@/database/schemas');
861
+ const [chunk] = await serverDB
862
+ .insert(chunks)
863
+ .values({
864
+ userId,
865
+ text: 'test chunk content',
866
+ })
867
+ .returning();
868
+
869
+ // 创建 query (需要 queryId)
870
+ const [query] = await serverDB
871
+ .insert(messageQueries)
872
+ .values({
873
+ messageId: msg1.id,
874
+ userId,
875
+ userQuery: 'test query',
876
+ })
877
+ .returning();
878
+
879
+ const result = await caller.updateMessageRAG({
880
+ id: msg1.id,
881
+ sessionId: testSessionId,
882
+ value: {
883
+ fileChunks: [{ id: chunk.id, similarity: 0.95 }],
884
+ ragQueryId: query.id,
885
+ },
886
+ });
887
+
888
+ expect(result.success).toBe(true);
889
+ expect(result.messages).toBeDefined();
890
+ expect(result.messages).toHaveLength(2);
891
+ });
892
+ });
893
+
894
+ describe('updateMetadata', () => {
895
+ it('should update message metadata', async () => {
896
+ const caller = messageRouter.createCaller(createTestContext(userId));
897
+
898
+ const msg = await caller.createNewMessage({
899
+ content: 'Message with metadata',
900
+ role: 'user',
901
+ sessionId: testSessionId,
902
+ });
903
+
904
+ await caller.updateMetadata({
905
+ id: msg.id,
906
+ value: { customKey: 'customValue' },
907
+ });
908
+
909
+ const [updatedMessage] = await serverDB
910
+ .select()
911
+ .from(messages)
912
+ .where(eq(messages.id, msg.id));
913
+
914
+ expect(updatedMessage).toBeDefined();
915
+ // Verify the message still exists after update
916
+ expect(updatedMessage.id).toBe(msg.id);
917
+ });
918
+ });
919
+
920
+ describe('updatePluginError', () => {
921
+ it('should update plugin error state', async () => {
922
+ const caller = messageRouter.createCaller(createTestContext(userId));
923
+
924
+ const msg = await caller.createNewMessage({
925
+ content: 'Message with plugin error',
926
+ role: 'assistant',
927
+ sessionId: testSessionId,
928
+ });
929
+
930
+ // 先创建一个 plugin 记录
931
+ const { messagePlugins } = await import('@/database/schemas');
932
+ await serverDB.insert(messagePlugins).values({
933
+ id: msg.id,
934
+ userId,
935
+ toolCallId: 'test-tool-call-error',
936
+ type: 'default',
937
+ });
938
+
939
+ await caller.updatePluginError({
940
+ id: msg.id,
941
+ value: { message: 'Plugin error occurred' },
942
+ });
943
+
944
+ const [updatedPlugin] = await serverDB
945
+ .select()
946
+ .from(messagePlugins)
947
+ .where(eq(messagePlugins.id, msg.id));
948
+
949
+ expect(updatedPlugin).toBeDefined();
950
+ expect(updatedPlugin.error).toBeDefined();
951
+ });
952
+
953
+ it('should return message list when sessionId is provided', async () => {
954
+ const caller = messageRouter.createCaller(createTestContext(userId));
955
+
956
+ const msg1 = await caller.createNewMessage({
957
+ content: 'Message 1',
958
+ role: 'assistant',
959
+ sessionId: testSessionId,
960
+ });
961
+
962
+ // 先创建一个 plugin 记录
963
+ const { messagePlugins } = await import('@/database/schemas');
964
+ await serverDB.insert(messagePlugins).values({
965
+ id: msg1.id,
966
+ userId,
967
+ toolCallId: 'test-tool-call-error-2',
968
+ type: 'default',
969
+ });
970
+
971
+ await caller.createNewMessage({
972
+ content: 'Message 2',
973
+ role: 'assistant',
974
+ sessionId: testSessionId,
975
+ });
976
+
977
+ const result = await caller.updatePluginError({
978
+ id: msg1.id,
979
+ sessionId: testSessionId,
980
+ value: { message: 'Error' },
981
+ });
982
+
983
+ expect(result.success).toBe(true);
984
+ expect(result.messages).toBeDefined();
985
+ expect(result.messages).toHaveLength(2);
986
+ });
987
+ });
988
+
989
+ describe('updatePluginState', () => {
990
+ it('should update plugin state', async () => {
991
+ const caller = messageRouter.createCaller(createTestContext(userId));
992
+
993
+ const msg = await caller.createNewMessage({
994
+ content: 'Message with plugin state',
995
+ role: 'assistant',
996
+ sessionId: testSessionId,
997
+ });
998
+
999
+ // 先创建一个 plugin 记录
1000
+ const { messagePlugins } = await import('@/database/schemas');
1001
+ await serverDB.insert(messagePlugins).values({
1002
+ id: msg.id,
1003
+ userId,
1004
+ toolCallId: 'test-tool-call-state',
1005
+ type: 'default',
1006
+ });
1007
+
1008
+ const result = await caller.updatePluginState({
1009
+ id: msg.id,
1010
+ sessionId: testSessionId,
1011
+ value: { stateKey: 'stateValue' },
1012
+ });
1013
+
1014
+ expect(result).toBeDefined();
1015
+ });
1016
+ });
1017
+
1018
+ describe('updateTTS', () => {
1019
+ it('should update TTS information', async () => {
1020
+ const caller = messageRouter.createCaller(createTestContext(userId));
1021
+
1022
+ const msg = await caller.createNewMessage({
1023
+ content: 'Message with TTS',
1024
+ role: 'assistant',
1025
+ sessionId: testSessionId,
1026
+ });
1027
+
1028
+ // 创建 file 记录
1029
+ const { files } = await import('@/database/schemas');
1030
+ const [file] = await serverDB
1031
+ .insert(files)
1032
+ .values({
1033
+ userId,
1034
+ name: 'audio.mp3',
1035
+ fileType: 'audio/mpeg',
1036
+ size: 1024,
1037
+ url: '/files/audio.mp3',
1038
+ })
1039
+ .returning();
1040
+
1041
+ await caller.updateTTS({
1042
+ id: msg.id,
1043
+ value: {
1044
+ file: file.id,
1045
+ voice: 'en-US-neural',
1046
+ contentMd5: 'abc123',
1047
+ },
1048
+ });
1049
+
1050
+ const { messageTTS } = await import('@/database/schemas');
1051
+ const [ttsRecord] = await serverDB.select().from(messageTTS).where(eq(messageTTS.id, msg.id));
1052
+
1053
+ expect(ttsRecord).toBeDefined();
1054
+ expect(ttsRecord.voice).toBe('en-US-neural');
1055
+ expect(ttsRecord.fileId).toBe(file.id);
1056
+ });
1057
+
1058
+ it('should delete TTS when value is false', async () => {
1059
+ const caller = messageRouter.createCaller(createTestContext(userId));
1060
+
1061
+ const msg = await caller.createNewMessage({
1062
+ content: 'Message with TTS to delete',
1063
+ role: 'assistant',
1064
+ sessionId: testSessionId,
1065
+ });
1066
+
1067
+ // 创建 file 记录
1068
+ const { files } = await import('@/database/schemas');
1069
+ const [file] = await serverDB
1070
+ .insert(files)
1071
+ .values({
1072
+ userId,
1073
+ name: 'audio-delete.mp3',
1074
+ fileType: 'audio/mpeg',
1075
+ size: 1024,
1076
+ url: '/files/audio-delete.mp3',
1077
+ })
1078
+ .returning();
1079
+
1080
+ // First add TTS
1081
+ await caller.updateTTS({
1082
+ id: msg.id,
1083
+ value: {
1084
+ file: file.id,
1085
+ voice: 'en-US-neural',
1086
+ },
1087
+ });
1088
+
1089
+ // Then delete it
1090
+ await caller.updateTTS({
1091
+ id: msg.id,
1092
+ value: false,
1093
+ });
1094
+
1095
+ const { messageTTS } = await import('@/database/schemas');
1096
+ const [ttsRecord] = await serverDB.select().from(messageTTS).where(eq(messageTTS.id, msg.id));
1097
+
1098
+ expect(ttsRecord).toBeUndefined();
1099
+ });
1100
+ });
1101
+
1102
+ describe('updateTranslate', () => {
1103
+ it('should update translation information', async () => {
1104
+ const caller = messageRouter.createCaller(createTestContext(userId));
1105
+
1106
+ const msg = await caller.createNewMessage({
1107
+ content: 'Hello world',
1108
+ role: 'user',
1109
+ sessionId: testSessionId,
1110
+ });
1111
+
1112
+ await caller.updateTranslate({
1113
+ id: msg.id,
1114
+ value: {
1115
+ content: '你好世界',
1116
+ from: 'en',
1117
+ to: 'zh',
1118
+ },
1119
+ });
1120
+
1121
+ const { messageTranslates } = await import('@/database/schemas');
1122
+ const [translateRecord] = await serverDB
1123
+ .select()
1124
+ .from(messageTranslates)
1125
+ .where(eq(messageTranslates.id, msg.id));
1126
+
1127
+ expect(translateRecord).toBeDefined();
1128
+ expect(translateRecord.to).toBe('zh');
1129
+ });
1130
+
1131
+ it('should delete translation when value is false', async () => {
1132
+ const caller = messageRouter.createCaller(createTestContext(userId));
1133
+
1134
+ const msg = await caller.createNewMessage({
1135
+ content: 'Hello world',
1136
+ role: 'user',
1137
+ sessionId: testSessionId,
1138
+ });
1139
+
1140
+ // First add translation
1141
+ await caller.updateTranslate({
1142
+ id: msg.id,
1143
+ value: {
1144
+ content: '你好世界',
1145
+ to: 'zh',
1146
+ },
1147
+ });
1148
+
1149
+ // Then delete it
1150
+ await caller.updateTranslate({
1151
+ id: msg.id,
1152
+ value: false,
1153
+ });
1154
+
1155
+ const [updatedMessage] = await serverDB
1156
+ .select()
1157
+ .from(messages)
1158
+ .where(eq(messages.id, msg.id));
1159
+
1160
+ expect(updatedMessage).toBeDefined();
1161
+ });
1162
+ });
1163
+
1164
+ describe('getHeatmaps', () => {
1165
+ it('should get message heatmaps', async () => {
1166
+ const caller = messageRouter.createCaller(createTestContext(userId));
1167
+
1168
+ // 创建一些消息
1169
+ await caller.createNewMessage({
1170
+ content: 'Message 1',
1171
+ role: 'user',
1172
+ sessionId: testSessionId,
1173
+ });
1174
+
1175
+ await caller.createNewMessage({
1176
+ content: 'Message 2',
1177
+ role: 'assistant',
1178
+ sessionId: testSessionId,
1179
+ });
1180
+
1181
+ const heatmaps = await caller.getHeatmaps();
1182
+
1183
+ expect(heatmaps).toBeDefined();
1184
+ expect(Array.isArray(heatmaps)).toBe(true);
1185
+ });
1186
+ });
1187
+
1188
+ describe('rankModels', () => {
1189
+ it('should get model usage ranking', async () => {
1190
+ const caller = messageRouter.createCaller(createTestContext(userId));
1191
+
1192
+ // 创建带有模型信息的消息
1193
+ const msg = await caller.createNewMessage({
1194
+ content: 'Message from AI',
1195
+ role: 'assistant',
1196
+ sessionId: testSessionId,
1197
+ });
1198
+
1199
+ // 添加模型信息
1200
+ await serverDB.update(messages).set({ model: 'gpt-4' }).where(eq(messages.id, msg.id));
1201
+
1202
+ const ranking = await caller.rankModels();
1203
+
1204
+ expect(ranking).toBeDefined();
1205
+ expect(Array.isArray(ranking)).toBe(true);
1206
+ });
1207
+ });
1208
+
468
1209
  describe('count and statistics', () => {
469
1210
  it('should count messages', async () => {
470
1211
  const caller = messageRouter.createCaller(createTestContext(userId));
@@ -487,6 +1228,26 @@ describe('Message Router Integration Tests', () => {
487
1228
  expect(count).toBe(2);
488
1229
  });
489
1230
 
1231
+ it('should count messages with date range', async () => {
1232
+ const caller = messageRouter.createCaller(createTestContext(userId));
1233
+
1234
+ await caller.createNewMessage({
1235
+ content: 'Message 1',
1236
+ role: 'user',
1237
+ sessionId: testSessionId,
1238
+ });
1239
+
1240
+ const startDate = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
1241
+ const endDate = new Date().toISOString();
1242
+
1243
+ const count = await caller.count({
1244
+ startDate,
1245
+ endDate,
1246
+ });
1247
+
1248
+ expect(count).toBeGreaterThanOrEqual(1);
1249
+ });
1250
+
490
1251
  it('should count words', async () => {
491
1252
  const caller = messageRouter.createCaller(createTestContext(userId));
492
1253
 
@@ -500,5 +1261,25 @@ describe('Message Router Integration Tests', () => {
500
1261
 
501
1262
  expect(wordCount).toBeGreaterThan(0);
502
1263
  });
1264
+
1265
+ it('should count words with date range', async () => {
1266
+ const caller = messageRouter.createCaller(createTestContext(userId));
1267
+
1268
+ await caller.createNewMessage({
1269
+ content: 'Hello world test message',
1270
+ role: 'user',
1271
+ sessionId: testSessionId,
1272
+ });
1273
+
1274
+ const startDate = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
1275
+ const endDate = new Date().toISOString();
1276
+
1277
+ const wordCount = await caller.countWords({
1278
+ startDate,
1279
+ endDate,
1280
+ });
1281
+
1282
+ expect(wordCount).toBeGreaterThan(0);
1283
+ });
503
1284
  });
504
1285
  });