@lobehub/chat 1.37.0 → 1.37.2

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.
Files changed (50) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/changelog/v1.json +18 -0
  3. package/locales/en-US/common.json +2 -2
  4. package/package.json +1 -1
  5. package/src/services/file/_deprecated.test.ts +119 -0
  6. package/src/services/file/{pglite.ts → _deprecated.ts} +28 -32
  7. package/src/services/file/client.test.ts +153 -74
  8. package/src/services/file/client.ts +32 -28
  9. package/src/services/file/index.ts +2 -2
  10. package/src/services/import/_deprecated.ts +74 -0
  11. package/src/services/import/{pglite.test.ts → client.test.ts} +1 -1
  12. package/src/services/import/client.ts +21 -61
  13. package/src/services/import/index.ts +2 -2
  14. package/src/services/message/_deprecated.test.ts +398 -0
  15. package/src/services/message/_deprecated.ts +121 -0
  16. package/src/services/message/client.test.ts +191 -159
  17. package/src/services/message/client.ts +47 -50
  18. package/src/services/message/index.ts +2 -2
  19. package/src/services/plugin/_deprecated.test.ts +162 -0
  20. package/src/services/plugin/_deprecated.ts +42 -0
  21. package/src/services/plugin/client.test.ts +68 -55
  22. package/src/services/plugin/client.ts +20 -11
  23. package/src/services/plugin/index.ts +2 -2
  24. package/src/services/session/_deprecated.test.ts +440 -0
  25. package/src/services/session/_deprecated.ts +183 -0
  26. package/src/services/session/client.test.ts +212 -241
  27. package/src/services/session/client.ts +61 -60
  28. package/src/services/session/index.ts +2 -2
  29. package/src/services/topic/{client.test.ts → _deprecated.test.ts} +1 -1
  30. package/src/services/topic/_deprecated.ts +70 -0
  31. package/src/services/topic/client.ts +40 -25
  32. package/src/services/topic/index.ts +2 -2
  33. package/src/services/topic/pglite.test.ts +1 -1
  34. package/src/services/user/{pglite.test.ts → _deprecated.test.ts} +32 -29
  35. package/src/services/user/_deprecated.ts +57 -0
  36. package/src/services/user/client.test.ts +28 -31
  37. package/src/services/user/client.ts +51 -16
  38. package/src/services/user/index.ts +2 -2
  39. package/src/store/chat/slices/builtinTool/action.test.ts +1 -1
  40. package/src/store/user/slices/common/action.test.ts +1 -1
  41. package/src/services/file/pglite.test.ts +0 -198
  42. package/src/services/import/pglite.ts +0 -34
  43. package/src/services/message/pglite.test.ts +0 -430
  44. package/src/services/message/pglite.ts +0 -118
  45. package/src/services/plugin/pglite.test.ts +0 -175
  46. package/src/services/plugin/pglite.ts +0 -51
  47. package/src/services/session/pglite.test.ts +0 -411
  48. package/src/services/session/pglite.ts +0 -184
  49. package/src/services/topic/pglite.ts +0 -85
  50. package/src/services/user/pglite.ts +0 -92
@@ -0,0 +1,121 @@
1
+ import dayjs from 'dayjs';
2
+
3
+ import { FileModel } from '@/database/_deprecated/models/file';
4
+ import { MessageModel } from '@/database/_deprecated/models/message';
5
+ import { DB_Message } from '@/database/_deprecated/schemas/message';
6
+ import {
7
+ ChatFileItem,
8
+ ChatMessage,
9
+ ChatMessageError,
10
+ ChatTTS,
11
+ ChatTranslate,
12
+ CreateMessageParams,
13
+ } from '@/types/message';
14
+
15
+ import { IMessageService } from './type';
16
+
17
+ export class ClientService implements IMessageService {
18
+ async createMessage(data: CreateMessageParams) {
19
+ const { id } = await MessageModel.create(data);
20
+
21
+ return id;
22
+ }
23
+
24
+ // @ts-ignore
25
+ async batchCreateMessages(messages: ChatMessage[]) {
26
+ return MessageModel.batchCreate(messages);
27
+ }
28
+
29
+ async getMessages(sessionId: string, topicId?: string): Promise<ChatMessage[]> {
30
+ const messages = await MessageModel.query({ sessionId, topicId });
31
+
32
+ const fileList = (await Promise.all(
33
+ messages
34
+ .flatMap((item) => item.files)
35
+ .filter(Boolean)
36
+ .map(async (id) => FileModel.findById(id!)),
37
+ )) as ChatFileItem[];
38
+
39
+ return messages.map((item) => ({
40
+ ...item,
41
+ imageList: fileList
42
+ .filter((file) => item.files?.includes(file.id) && file.fileType.startsWith('image'))
43
+ .map((file) => ({
44
+ alt: file.name,
45
+ id: file.id,
46
+ url: file.url,
47
+ })),
48
+ }));
49
+ }
50
+
51
+ async getAllMessages() {
52
+ return MessageModel.queryAll();
53
+ }
54
+
55
+ async countMessages() {
56
+ return MessageModel.count();
57
+ }
58
+
59
+ async countTodayMessages() {
60
+ const topics = await MessageModel.queryAll();
61
+ return topics.filter(
62
+ (item) => dayjs(item.createdAt).format('YYYY-MM-DD') === dayjs().format('YYYY-MM-DD'),
63
+ ).length;
64
+ }
65
+
66
+ async getAllMessagesInSession(sessionId: string) {
67
+ return MessageModel.queryBySessionId(sessionId);
68
+ }
69
+
70
+ async updateMessageError(id: string, error: ChatMessageError) {
71
+ return MessageModel.update(id, { error });
72
+ }
73
+
74
+ // @ts-ignore
75
+ async updateMessage(id: string, message: Partial<DB_Message>) {
76
+ return MessageModel.update(id, message);
77
+ }
78
+
79
+ async updateMessageTTS(id: string, tts: Partial<ChatTTS> | false) {
80
+ return MessageModel.update(id, { tts });
81
+ }
82
+
83
+ async updateMessageTranslate(id: string, translate: Partial<ChatTranslate> | false) {
84
+ return MessageModel.update(id, { translate });
85
+ }
86
+
87
+ async updateMessagePluginState(id: string, value: Record<string, any>) {
88
+ return MessageModel.updatePluginState(id, value);
89
+ }
90
+
91
+ async updateMessagePluginArguments(id: string, value: string | Record<string, any>) {
92
+ const args = typeof value === 'string' ? value : JSON.stringify(value);
93
+
94
+ return MessageModel.updatePlugin(id, { arguments: args });
95
+ }
96
+
97
+ async bindMessagesToTopic(topicId: string, messageIds: string[]) {
98
+ return MessageModel.batchUpdate(messageIds, { topicId });
99
+ }
100
+
101
+ async removeMessage(id: string) {
102
+ return MessageModel.delete(id);
103
+ }
104
+
105
+ async removeMessages(ids: string[]) {
106
+ return MessageModel.bulkDelete(ids);
107
+ }
108
+
109
+ async removeMessagesByAssistant(assistantId: string, topicId?: string) {
110
+ return MessageModel.batchDelete(assistantId, topicId);
111
+ }
112
+
113
+ async removeAllMessages() {
114
+ return MessageModel.clearTable();
115
+ }
116
+
117
+ async hasMessages() {
118
+ const number = await this.countMessages();
119
+ return number > 0;
120
+ }
121
+ }
@@ -1,133 +1,155 @@
1
1
  import dayjs from 'dayjs';
2
- import { Mock, describe, expect, it, vi } from 'vitest';
2
+ import { and, eq } from 'drizzle-orm';
3
+ import { Mock, afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3
4
 
4
- import { CreateMessageParams, MessageModel } from '@/database/_deprecated/models/message';
5
+ import { MessageModel } from '@/database/_deprecated/models/message';
6
+ import { clientDB, initializeDB } from '@/database/client/db';
7
+ import {
8
+ MessageItem,
9
+ files,
10
+ messagePlugins,
11
+ messageTTS,
12
+ messageTranslates,
13
+ messages,
14
+ sessions,
15
+ topics,
16
+ users,
17
+ } from '@/database/schemas';
5
18
  import {
6
19
  ChatMessage,
7
20
  ChatMessageError,
8
- ChatPluginPayload,
9
21
  ChatTTS,
10
22
  ChatTranslate,
23
+ CreateMessageParams,
11
24
  } from '@/types/message';
12
25
 
13
26
  import { ClientService } from './client';
14
27
 
15
- const messageService = new ClientService();
16
-
17
- // Mock the MessageModel
18
- vi.mock('@/database/_deprecated/models/message', () => {
19
- return {
20
- MessageModel: {
21
- create: vi.fn(),
22
- batchCreate: vi.fn(),
23
- count: vi.fn(),
24
- query: vi.fn(),
25
- delete: vi.fn(),
26
- bulkDelete: vi.fn(),
27
- queryBySessionId: vi.fn(),
28
- update: vi.fn(),
29
- updatePlugin: vi.fn(),
30
- batchDelete: vi.fn(),
31
- clearTable: vi.fn(),
32
- batchUpdate: vi.fn(),
33
- queryAll: vi.fn(),
34
- updatePluginState: vi.fn(),
35
- },
36
- };
28
+ const userId = 'message-db';
29
+ const sessionId = '1';
30
+ const topicId = 'topic-id';
31
+
32
+ // Mock data
33
+ const mockMessageId = 'mock-message-id';
34
+ const mockMessage = {
35
+ id: mockMessageId,
36
+ content: 'Mock message content',
37
+ sessionId,
38
+ role: 'user',
39
+ } as ChatMessage;
40
+
41
+ const mockMessages = [mockMessage];
42
+
43
+ beforeEach(async () => {
44
+ await initializeDB();
45
+
46
+ // 在每个测试用例之前,清空表
47
+ await clientDB.transaction(async (trx) => {
48
+ await trx.delete(users);
49
+ await trx.insert(users).values([{ id: userId }, { id: '456' }]);
50
+
51
+ await trx.insert(sessions).values([{ id: sessionId, userId }]);
52
+ await trx.insert(topics).values([{ id: topicId, sessionId, userId }]);
53
+ await trx.insert(files).values({
54
+ id: 'f1',
55
+ userId: userId,
56
+ url: 'abc',
57
+ name: 'file-1',
58
+ fileType: 'image/png',
59
+ size: 1000,
60
+ });
61
+ });
37
62
  });
38
63
 
39
- describe('MessageClientService', () => {
40
- // Mock data
41
- const mockMessageId = 'mock-message-id';
42
- const mockMessage = {
43
- id: mockMessageId,
44
- content: 'Mock message content',
45
- sessionId: 'mock-session-id',
46
- createdAt: 100,
47
- updatedAt: 100,
48
- role: 'user',
49
- // ... other properties
50
- } as ChatMessage;
51
- const mockMessages = [mockMessage];
52
-
53
- beforeEach(() => {
54
- // Reset all mocks before running each test case
55
- vi.resetAllMocks();
56
- });
64
+ afterEach(async () => {
65
+ // 在每个测试用例之后,清空表
66
+ await clientDB.delete(users);
67
+ });
68
+
69
+ const messageService = new ClientService(userId);
57
70
 
71
+ describe('MessageClientService', () => {
58
72
  describe('create', () => {
59
73
  it('should create a message and return its id', async () => {
60
74
  // Setup
61
- const createParams = {
75
+ const createParams: CreateMessageParams = {
62
76
  content: 'New message content',
63
- sessionId: '1',
64
- // ... other properties
65
- } as CreateMessageParams;
66
- (MessageModel.create as Mock).mockResolvedValue({ id: mockMessageId });
77
+ sessionId,
78
+ role: 'user',
79
+ };
67
80
 
68
81
  // Execute
69
82
  const messageId = await messageService.createMessage(createParams);
70
83
 
71
84
  // Assert
72
- expect(MessageModel.create).toHaveBeenCalledWith(createParams);
73
- expect(messageId).toBe(mockMessageId);
85
+ expect(messageId).toMatch(/^msg_/);
74
86
  });
75
87
  });
76
88
 
77
89
  describe('batchCreate', () => {
78
90
  it('should batch create messages', async () => {
79
- // Setup
80
- (MessageModel.batchCreate as Mock).mockResolvedValue(mockMessages);
81
-
82
91
  // Execute
83
- const result = await messageService.batchCreateMessages(mockMessages);
92
+ await messageService.batchCreateMessages([
93
+ {
94
+ content: 'Mock message content',
95
+ sessionId,
96
+ role: 'user',
97
+ },
98
+ {
99
+ content: 'Mock message content',
100
+ sessionId,
101
+ role: 'user',
102
+ },
103
+ ] as MessageItem[]);
104
+ const count = await clientDB.$count(messages);
84
105
 
85
106
  // Assert
86
- expect(MessageModel.batchCreate).toHaveBeenCalledWith(mockMessages);
87
- expect(result).toBe(mockMessages);
107
+ expect(count).toBe(2);
88
108
  });
89
109
  });
90
110
 
91
111
  describe('removeMessage', () => {
92
112
  it('should remove a message by id', async () => {
93
- // Setup
94
- (MessageModel.delete as Mock).mockResolvedValue(true);
95
-
96
113
  // Execute
97
- const result = await messageService.removeMessage(mockMessageId);
114
+ await clientDB.insert(messages).values({ id: mockMessageId, role: 'user', userId });
115
+ await messageService.removeMessage(mockMessageId);
98
116
 
99
117
  // Assert
100
- expect(MessageModel.delete).toHaveBeenCalledWith(mockMessageId);
101
- expect(result).toBe(true);
118
+ const count = await clientDB.$count(messages);
119
+
120
+ expect(count).toBe(0);
102
121
  });
103
122
  });
104
123
  describe('removeMessages', () => {
105
124
  it('should remove a message by id', async () => {
106
125
  // Setup
107
- (MessageModel.bulkDelete as Mock).mockResolvedValue(true);
126
+ await clientDB.insert(messages).values([
127
+ { id: mockMessageId, role: 'user', userId },
128
+ { role: 'assistant', userId },
129
+ ]);
108
130
 
109
131
  // Execute
110
- const result = await messageService.removeMessages([mockMessageId]);
132
+ await messageService.removeMessages([mockMessageId]);
111
133
 
112
134
  // Assert
113
- expect(MessageModel.bulkDelete).toHaveBeenCalledWith([mockMessageId]);
114
- expect(result).toBe(true);
135
+ const count = await clientDB.$count(messages);
136
+
137
+ expect(count).toBe(1);
115
138
  });
116
139
  });
117
140
 
118
141
  describe('getMessages', () => {
119
142
  it('should retrieve messages by sessionId and topicId', async () => {
120
143
  // Setup
121
- const sessionId = 'session-id';
122
- const topicId = 'topic-id';
123
- (MessageModel.query as Mock).mockResolvedValue(mockMessages);
144
+ await clientDB
145
+ .insert(messages)
146
+ .values({ id: mockMessageId, sessionId, topicId, role: 'user', userId });
124
147
 
125
148
  // Execute
126
- const messages = await messageService.getMessages(sessionId, topicId);
149
+ const data = await messageService.getMessages(sessionId, topicId);
127
150
 
128
151
  // Assert
129
- expect(MessageModel.query).toHaveBeenCalledWith({ sessionId, topicId });
130
- expect(messages).toEqual(mockMessages.map((i) => ({ ...i, imageList: [] })));
152
+ expect(data[0]).toMatchObject({ id: mockMessageId, role: 'user' });
131
153
  });
132
154
  });
133
155
 
@@ -135,14 +157,21 @@ describe('MessageClientService', () => {
135
157
  it('should retrieve all messages in a session', async () => {
136
158
  // Setup
137
159
  const sessionId = 'session-id';
138
- (MessageModel.queryBySessionId as Mock).mockResolvedValue(mockMessages);
160
+ await clientDB.insert(sessions).values([
161
+ { id: 'bbb', userId },
162
+ { id: sessionId, userId },
163
+ ]);
164
+ await clientDB.insert(messages).values([
165
+ { sessionId, topicId, role: 'user', userId },
166
+ { sessionId, topicId, role: 'assistant', userId },
167
+ { sessionId: 'bbb', topicId, role: 'assistant', userId },
168
+ ]);
139
169
 
140
170
  // Execute
141
- const messages = await messageService.getAllMessagesInSession(sessionId);
171
+ const data = await messageService.getAllMessagesInSession(sessionId);
142
172
 
143
173
  // Assert
144
- expect(MessageModel.queryBySessionId).toHaveBeenCalledWith(sessionId);
145
- expect(messages).toBe(mockMessages);
174
+ expect(data.length).toBe(2);
146
175
  });
147
176
  });
148
177
 
@@ -150,77 +179,85 @@ describe('MessageClientService', () => {
150
179
  it('should batch remove messages by assistantId and topicId', async () => {
151
180
  // Setup
152
181
  const assistantId = 'assistant-id';
153
- const topicId = 'topic-id';
154
- (MessageModel.batchDelete as Mock).mockResolvedValue(true);
182
+ const sessionId = 'session-id';
183
+ await clientDB.insert(sessions).values([
184
+ { id: 'bbb', userId },
185
+ { id: sessionId, userId },
186
+ ]);
187
+ await clientDB.insert(messages).values([
188
+ { sessionId, topicId, role: 'user', userId },
189
+ { sessionId, topicId, role: 'assistant', userId },
190
+ { sessionId: 'bbb', topicId, role: 'assistant', userId },
191
+ ]);
155
192
 
156
193
  // Execute
157
- const result = await messageService.removeMessagesByAssistant(assistantId, topicId);
194
+ await messageService.removeMessagesByAssistant(sessionId, topicId);
158
195
 
159
196
  // Assert
160
- expect(MessageModel.batchDelete).toHaveBeenCalledWith(assistantId, topicId);
161
- expect(result).toBe(true);
197
+ const result = await clientDB.query.messages.findMany({
198
+ where: and(eq(messages.sessionId, sessionId), eq(messages.topicId, topicId)),
199
+ });
200
+
201
+ expect(result.length).toBe(0);
162
202
  });
163
203
  });
164
204
 
165
205
  describe('clearAllMessage', () => {
166
206
  it('should clear all messages from the table', async () => {
167
207
  // Setup
168
- (MessageModel.clearTable as Mock).mockResolvedValue(true);
208
+ await clientDB.insert(users).values({ id: 'another' });
209
+ await clientDB.insert(messages).values([
210
+ { id: mockMessageId, role: 'user', userId },
211
+ { role: 'user', userId: 'another' },
212
+ ]);
169
213
 
170
214
  // Execute
171
- const result = await messageService.removeAllMessages();
215
+ await messageService.removeAllMessages();
172
216
 
173
217
  // Assert
174
- expect(MessageModel.clearTable).toHaveBeenCalled();
175
- expect(result).toBe(true);
176
- });
177
- });
178
-
179
- describe('bindMessagesToTopic', () => {
180
- it('should batch update messages to bind them to a topic', async () => {
181
- // Setup
182
- const topicId = 'topic-id';
183
- const messageIds = [mockMessageId];
184
- (MessageModel.batchUpdate as Mock).mockResolvedValue(mockMessages);
185
-
186
- // Execute
187
- const result = await messageService.bindMessagesToTopic(topicId, messageIds);
188
-
189
- // Assert
190
- expect(MessageModel.batchUpdate).toHaveBeenCalledWith(messageIds, { topicId });
191
- expect(result).toBe(mockMessages);
218
+ const result = await clientDB.query.messages.findMany({
219
+ where: eq(messages.userId, userId),
220
+ });
221
+ expect(result.length).toBe(0);
192
222
  });
193
223
  });
194
224
 
195
225
  describe('getAllMessages', () => {
196
226
  it('should retrieve all messages', async () => {
197
- // Setup
198
- (MessageModel.queryAll as Mock).mockResolvedValue(mockMessages);
227
+ await clientDB.insert(messages).values([
228
+ { sessionId, topicId, content: '1', role: 'user', userId },
229
+ { sessionId, topicId, content: '2', role: 'assistant', userId },
230
+ ]);
199
231
 
200
232
  // Execute
201
- const messages = await messageService.getAllMessages();
233
+ const data = await messageService.getAllMessages();
202
234
 
203
235
  // Assert
204
- expect(MessageModel.queryAll).toHaveBeenCalled();
205
- expect(messages).toBe(mockMessages);
236
+ expect(data).toMatchObject([
237
+ { sessionId, topicId, content: '1', role: 'user', userId },
238
+ { sessionId, topicId, content: '2', role: 'assistant', userId },
239
+ ]);
206
240
  });
207
241
  });
208
242
 
209
243
  describe('updateMessageError', () => {
210
244
  it('should update the error field of a message', async () => {
211
245
  // Setup
246
+ await clientDB.insert(messages).values({ id: mockMessageId, role: 'user', userId });
212
247
  const newError = {
213
248
  type: 'InvalidProviderAPIKey',
214
249
  message: 'Error occurred',
215
250
  } as ChatMessageError;
216
- (MessageModel.update as Mock).mockResolvedValue({ ...mockMessage, error: newError });
217
251
 
218
252
  // Execute
219
- const result = await messageService.updateMessageError(mockMessageId, newError);
253
+ await messageService.updateMessageError(mockMessageId, newError);
220
254
 
221
255
  // Assert
222
- expect(MessageModel.update).toHaveBeenCalledWith(mockMessageId, { error: newError });
223
- expect(result).toEqual({ ...mockMessage, error: newError });
256
+ const result = await clientDB.query.messages.findFirst({
257
+ where: eq(messages.id, mockMessageId),
258
+ });
259
+
260
+ expect(result!.error).toEqual(newError);
224
261
  });
225
262
  });
226
263
 
@@ -248,88 +285,85 @@ describe('MessageClientService', () => {
248
285
  describe('updateMessagePluginState', () => {
249
286
  it('should update the plugin state of a message', async () => {
250
287
  // Setup
288
+ await clientDB.insert(messages).values({ id: mockMessageId, role: 'user', userId });
289
+ await clientDB.insert(messagePlugins).values({ id: mockMessageId });
251
290
  const key = 'stateKey';
252
291
  const value = 'stateValue';
253
292
  const newPluginState = { [key]: value };
254
- (MessageModel.updatePluginState as Mock).mockResolvedValue({
255
- ...mockMessage,
256
- pluginState: newPluginState,
257
- });
258
293
 
259
294
  // Execute
260
- const result = await messageService.updateMessagePluginState(mockMessageId, { key: value });
295
+ await messageService.updateMessagePluginState(mockMessageId, { stateKey: value });
261
296
 
262
297
  // Assert
263
- expect(MessageModel.updatePluginState).toHaveBeenCalledWith(mockMessageId, { key: value });
264
- expect(result).toEqual({ ...mockMessage, pluginState: newPluginState });
298
+ const result = await clientDB.query.messagePlugins.findFirst({
299
+ where: eq(messagePlugins.id, mockMessageId),
300
+ });
301
+ expect(result!.state).toEqual(newPluginState);
265
302
  });
266
303
  });
267
304
 
268
305
  describe('updateMessagePluginArguments', () => {
269
306
  it('should update the plugin arguments object of a message', async () => {
270
307
  // Setup
271
- const key = 'stateKey';
308
+ await clientDB.insert(messages).values({ id: mockMessageId, role: 'user', userId });
309
+ await clientDB.insert(messagePlugins).values({ id: mockMessageId });
272
310
  const value = 'stateValue';
273
- (MessageModel.updatePlugin as Mock).mockResolvedValue({});
274
311
 
275
312
  // Execute
276
313
  await messageService.updateMessagePluginArguments(mockMessageId, { key: value });
277
314
 
278
315
  // Assert
279
- expect(MessageModel.updatePlugin).toHaveBeenCalledWith(mockMessageId, {
280
- arguments: '{"key":"stateValue"}',
316
+ const result = await clientDB.query.messagePlugins.findFirst({
317
+ where: eq(messageTTS.id, mockMessageId),
281
318
  });
319
+ expect(result).toMatchObject({ arguments: '{"key":"stateValue"}' });
282
320
  });
283
321
  it('should update the plugin arguments string of a message', async () => {
284
322
  // Setup
285
- const key = 'stateKey';
323
+ await clientDB.insert(messages).values({ id: mockMessageId, role: 'user', userId });
324
+ await clientDB.insert(messagePlugins).values({ id: mockMessageId });
286
325
  const value = 'stateValue';
287
- (MessageModel.updatePlugin as Mock).mockResolvedValue({});
288
-
289
326
  // Execute
290
327
  await messageService.updateMessagePluginArguments(
291
328
  mockMessageId,
292
- JSON.stringify({ key: value }),
329
+ JSON.stringify({ abc: value }),
293
330
  );
294
331
 
295
332
  // Assert
296
- expect(MessageModel.updatePlugin).toHaveBeenCalledWith(mockMessageId, {
297
- arguments: '{"key":"stateValue"}',
333
+ const result = await clientDB.query.messagePlugins.findFirst({
334
+ where: eq(messageTTS.id, mockMessageId),
298
335
  });
336
+ expect(result).toMatchObject({ arguments: '{"abc":"stateValue"}' });
299
337
  });
300
338
  });
301
339
 
302
340
  describe('countMessages', () => {
303
341
  it('should count the total number of messages', async () => {
304
342
  // Setup
305
- const mockCount = 10;
306
- (MessageModel.count as Mock).mockResolvedValue(mockCount);
343
+ await clientDB.insert(messages).values({ id: mockMessageId, role: 'user', userId });
307
344
 
308
345
  // Execute
309
346
  const count = await messageService.countMessages();
310
347
 
311
348
  // Assert
312
- expect(MessageModel.count).toHaveBeenCalled();
313
- expect(count).toBe(mockCount);
349
+ expect(count).toBe(1);
314
350
  });
315
351
  });
316
352
 
317
353
  describe('countTodayMessages', () => {
318
354
  it('should count the number of messages created today', async () => {
319
355
  // Setup
320
- const today = dayjs().format('YYYY-MM-DD');
321
356
  const mockMessages = [
322
- { ...mockMessage, createdAt: today },
323
- { ...mockMessage, createdAt: today },
324
- { ...mockMessage, createdAt: '2023-01-01' },
357
+ { ...mockMessage, id: undefined, createdAt: new Date(), userId },
358
+ { ...mockMessage, id: undefined, createdAt: new Date(), userId },
359
+ { ...mockMessage, id: undefined, createdAt: new Date('2023-01-01'), userId },
325
360
  ];
326
- (MessageModel.queryAll as Mock).mockResolvedValue(mockMessages);
361
+ await clientDB.insert(messages).values(mockMessages);
327
362
 
328
363
  // Execute
329
364
  const count = await messageService.countTodayMessages();
330
365
 
331
366
  // Assert
332
- expect(MessageModel.queryAll).toHaveBeenCalled();
333
367
  expect(count).toBe(2);
334
368
  });
335
369
  });
@@ -337,45 +371,46 @@ describe('MessageClientService', () => {
337
371
  describe('updateMessageTTS', () => {
338
372
  it('should update the TTS field of a message', async () => {
339
373
  // Setup
340
- const newTTS: ChatTTS = {
341
- contentMd5: 'abc',
342
- file: 'file-abc',
343
- };
344
-
345
- (MessageModel.update as Mock).mockResolvedValue({ ...mockMessage, tts: newTTS });
374
+ await clientDB
375
+ .insert(files)
376
+ .values({ id: 'file-abc', fileType: 'text', name: 'abc', url: 'abc', size: 100, userId });
377
+ await clientDB.insert(messages).values({ id: mockMessageId, role: 'user', userId });
378
+ const newTTS: ChatTTS = { contentMd5: 'abc', file: 'file-abc' };
346
379
 
347
380
  // Execute
348
- const result = await messageService.updateMessageTTS(mockMessageId, newTTS);
381
+ await messageService.updateMessageTTS(mockMessageId, newTTS);
349
382
 
350
383
  // Assert
351
- expect(MessageModel.update).toHaveBeenCalledWith(mockMessageId, { tts: newTTS });
352
- expect(result).toEqual({ ...mockMessage, tts: newTTS });
384
+ const result = await clientDB.query.messageTTS.findFirst({
385
+ where: eq(messageTTS.id, mockMessageId),
386
+ });
387
+
388
+ expect(result).toMatchObject({ contentMd5: 'abc', fileId: 'file-abc', id: mockMessageId });
353
389
  });
354
390
  });
355
391
 
356
392
  describe('updateMessageTranslate', () => {
357
393
  it('should update the translate field of a message', async () => {
358
394
  // Setup
359
- const newTranslate: ChatTranslate = {
360
- content: 'Translated text',
361
- to: 'es',
362
- };
363
-
364
- (MessageModel.update as Mock).mockResolvedValue({ ...mockMessage, translate: newTranslate });
395
+ await clientDB.insert(messages).values({ id: mockMessageId, role: 'user', userId });
396
+ const newTranslate: ChatTranslate = { content: 'Translated text', to: 'es' };
365
397
 
366
398
  // Execute
367
- const result = await messageService.updateMessageTranslate(mockMessageId, newTranslate);
399
+ await messageService.updateMessageTranslate(mockMessageId, newTranslate);
368
400
 
369
401
  // Assert
370
- expect(MessageModel.update).toHaveBeenCalledWith(mockMessageId, { translate: newTranslate });
371
- expect(result).toEqual({ ...mockMessage, translate: newTranslate });
402
+ const result = await clientDB.query.messageTranslates.findFirst({
403
+ where: eq(messageTranslates.id, mockMessageId),
404
+ });
405
+
406
+ expect(result).toMatchObject(newTranslate);
372
407
  });
373
408
  });
374
409
 
375
410
  describe('hasMessages', () => {
376
411
  it('should return true if there are messages', async () => {
377
412
  // Setup
378
- (MessageModel.count as Mock).mockResolvedValue(1);
413
+ await clientDB.insert(messages).values({ id: mockMessageId, role: 'user', userId });
379
414
 
380
415
  // Execute
381
416
  const result = await messageService.hasMessages();
@@ -385,9 +420,6 @@ describe('MessageClientService', () => {
385
420
  });
386
421
 
387
422
  it('should return false if there are no messages', async () => {
388
- // Setup
389
- (MessageModel.count as Mock).mockResolvedValue(0);
390
-
391
423
  // Execute
392
424
  const result = await messageService.hasMessages();
393
425