@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.
- package/CHANGELOG.md +50 -0
- package/changelog/v1.json +18 -0
- package/locales/en-US/common.json +2 -2
- package/package.json +1 -1
- package/src/services/file/_deprecated.test.ts +119 -0
- package/src/services/file/{pglite.ts → _deprecated.ts} +28 -32
- package/src/services/file/client.test.ts +153 -74
- package/src/services/file/client.ts +32 -28
- package/src/services/file/index.ts +2 -2
- package/src/services/import/_deprecated.ts +74 -0
- package/src/services/import/{pglite.test.ts → client.test.ts} +1 -1
- package/src/services/import/client.ts +21 -61
- package/src/services/import/index.ts +2 -2
- package/src/services/message/_deprecated.test.ts +398 -0
- package/src/services/message/_deprecated.ts +121 -0
- package/src/services/message/client.test.ts +191 -159
- package/src/services/message/client.ts +47 -50
- package/src/services/message/index.ts +2 -2
- package/src/services/plugin/_deprecated.test.ts +162 -0
- package/src/services/plugin/_deprecated.ts +42 -0
- package/src/services/plugin/client.test.ts +68 -55
- package/src/services/plugin/client.ts +20 -11
- package/src/services/plugin/index.ts +2 -2
- package/src/services/session/_deprecated.test.ts +440 -0
- package/src/services/session/_deprecated.ts +183 -0
- package/src/services/session/client.test.ts +212 -241
- package/src/services/session/client.ts +61 -60
- package/src/services/session/index.ts +2 -2
- package/src/services/topic/{client.test.ts → _deprecated.test.ts} +1 -1
- package/src/services/topic/_deprecated.ts +70 -0
- package/src/services/topic/client.ts +40 -25
- package/src/services/topic/index.ts +2 -2
- package/src/services/topic/pglite.test.ts +1 -1
- package/src/services/user/{pglite.test.ts → _deprecated.test.ts} +32 -29
- package/src/services/user/_deprecated.ts +57 -0
- package/src/services/user/client.test.ts +28 -31
- package/src/services/user/client.ts +51 -16
- package/src/services/user/index.ts +2 -2
- package/src/store/chat/slices/builtinTool/action.test.ts +1 -1
- package/src/store/user/slices/common/action.test.ts +1 -1
- package/src/services/file/pglite.test.ts +0 -198
- package/src/services/import/pglite.ts +0 -34
- package/src/services/message/pglite.test.ts +0 -430
- package/src/services/message/pglite.ts +0 -118
- package/src/services/plugin/pglite.test.ts +0 -175
- package/src/services/plugin/pglite.ts +0 -51
- package/src/services/session/pglite.test.ts +0 -411
- package/src/services/session/pglite.ts +0 -184
- package/src/services/topic/pglite.ts +0 -85
- package/src/services/user/pglite.ts +0 -92
@@ -1,24 +1,31 @@
|
|
1
|
-
import {
|
1
|
+
import { clientDB } from '@/database/client/db';
|
2
|
+
import { FileModel } from '@/database/server/models/file';
|
3
|
+
import { BaseClientService } from '@/services/baseClientService';
|
2
4
|
import { clientS3Storage } from '@/services/file/ClientS3';
|
3
5
|
import { FileItem, UploadFileParams } from '@/types/files';
|
4
6
|
|
5
7
|
import { IFileService } from './type';
|
6
8
|
|
7
|
-
export class ClientService implements IFileService {
|
9
|
+
export class ClientService extends BaseClientService implements IFileService {
|
10
|
+
private get fileModel(): FileModel {
|
11
|
+
return new FileModel(clientDB as any, this.userId);
|
12
|
+
}
|
13
|
+
|
8
14
|
async createFile(file: UploadFileParams) {
|
9
15
|
// save to local storage
|
10
16
|
// we may want to save to a remote server later
|
11
|
-
const res = await
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
17
|
+
const res = await this.fileModel.create(
|
18
|
+
{
|
19
|
+
fileHash: file.hash,
|
20
|
+
fileType: file.fileType,
|
21
|
+
knowledgeBaseId: file.knowledgeBaseId,
|
22
|
+
metadata: file.metadata,
|
23
|
+
name: file.name,
|
24
|
+
size: file.size,
|
25
|
+
url: file.url!,
|
26
|
+
},
|
27
|
+
true,
|
28
|
+
);
|
22
29
|
|
23
30
|
// get file to base64 url
|
24
31
|
const base64 = await this.getBase64ByFileHash(file.hash!);
|
@@ -29,24 +36,17 @@ export class ClientService implements IFileService {
|
|
29
36
|
};
|
30
37
|
}
|
31
38
|
|
32
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
33
|
-
async checkFileHash(_hash: string) {
|
34
|
-
return { isExist: false, metadata: {} };
|
35
|
-
}
|
36
|
-
|
37
39
|
async getFile(id: string): Promise<FileItem> {
|
38
|
-
const item = await
|
40
|
+
const item = await this.fileModel.findById(id);
|
39
41
|
if (!item) {
|
40
42
|
throw new Error('file not found');
|
41
43
|
}
|
42
44
|
|
43
|
-
// arrayBuffer to
|
44
|
-
const
|
45
|
-
|
46
|
-
: // @ts-ignore
|
47
|
-
new Blob([Buffer.from(item.base64!, 'base64')], { type: item.fileType });
|
45
|
+
// arrayBuffer to url
|
46
|
+
const fileItem = await clientS3Storage.getObject(item.fileHash!);
|
47
|
+
if (!fileItem) throw new Error('file not found');
|
48
48
|
|
49
|
-
const url = URL.createObjectURL(
|
49
|
+
const url = URL.createObjectURL(fileItem);
|
50
50
|
|
51
51
|
return {
|
52
52
|
createdAt: new Date(item.createdAt),
|
@@ -60,15 +60,19 @@ export class ClientService implements IFileService {
|
|
60
60
|
}
|
61
61
|
|
62
62
|
async removeFile(id: string) {
|
63
|
-
|
63
|
+
await this.fileModel.delete(id, false);
|
64
64
|
}
|
65
65
|
|
66
66
|
async removeFiles(ids: string[]) {
|
67
|
-
await
|
67
|
+
await this.fileModel.deleteMany(ids, false);
|
68
68
|
}
|
69
69
|
|
70
70
|
async removeAllFiles() {
|
71
|
-
return
|
71
|
+
return this.fileModel.clear();
|
72
|
+
}
|
73
|
+
|
74
|
+
async checkFileHash(hash: string) {
|
75
|
+
return this.fileModel.checkHash(hash);
|
72
76
|
}
|
73
77
|
|
74
78
|
private async getBase64ByFileHash(hash: string) {
|
@@ -1,5 +1,5 @@
|
|
1
|
-
import { ClientService as DeprecatedService } from './
|
2
|
-
import { ClientService } from './
|
1
|
+
import { ClientService as DeprecatedService } from './_deprecated';
|
2
|
+
import { ClientService } from './client';
|
3
3
|
import { ServerService } from './server';
|
4
4
|
|
5
5
|
const clientService =
|
@@ -0,0 +1,74 @@
|
|
1
|
+
import { MessageModel } from '@/database/_deprecated/models/message';
|
2
|
+
import { SessionModel } from '@/database/_deprecated/models/session';
|
3
|
+
import { SessionGroupModel } from '@/database/_deprecated/models/sessionGroup';
|
4
|
+
import { TopicModel } from '@/database/_deprecated/models/topic';
|
5
|
+
import { ImportResult, ImportResults } from '@/services/config';
|
6
|
+
import { useUserStore } from '@/store/user';
|
7
|
+
import { ImportStage, ImporterEntryData, OnImportCallbacks } from '@/types/importer';
|
8
|
+
import { UserSettings } from '@/types/user/settings';
|
9
|
+
|
10
|
+
export class ClientService {
|
11
|
+
importSettings = async (settings: UserSettings) => {
|
12
|
+
await useUserStore.getState().importAppSettings(settings);
|
13
|
+
};
|
14
|
+
|
15
|
+
importData = async (
|
16
|
+
config: ImporterEntryData,
|
17
|
+
callbacks?: OnImportCallbacks,
|
18
|
+
): Promise<ImportResults> => {
|
19
|
+
callbacks?.onStageChange?.(ImportStage.Importing);
|
20
|
+
const time = Date.now();
|
21
|
+
|
22
|
+
const { messages = [], sessionGroups = [], sessions = [], topics = [] } = config;
|
23
|
+
|
24
|
+
let messageResult: ImportResult | undefined;
|
25
|
+
let sessionResult: ImportResult | undefined;
|
26
|
+
let sessionGroupResult: ImportResult | undefined;
|
27
|
+
let topicResult: ImportResult | undefined;
|
28
|
+
|
29
|
+
if (messages.length > 0) {
|
30
|
+
const res = await MessageModel.batchCreate(messages as any);
|
31
|
+
messageResult = this.mapImportResult(res);
|
32
|
+
}
|
33
|
+
|
34
|
+
if (sessionGroups.length > 0) {
|
35
|
+
const res = await SessionGroupModel.batchCreate(sessionGroups as any);
|
36
|
+
sessionGroupResult = this.mapImportResult(res);
|
37
|
+
}
|
38
|
+
|
39
|
+
if (topics.length > 0) {
|
40
|
+
const res = await TopicModel.batchCreate(topics as any);
|
41
|
+
topicResult = this.mapImportResult(res);
|
42
|
+
}
|
43
|
+
|
44
|
+
if (sessions.length > 0) {
|
45
|
+
const data = await SessionModel.batchCreate(sessions as any);
|
46
|
+
sessionResult = this.mapImportResult(data);
|
47
|
+
}
|
48
|
+
|
49
|
+
const result = {
|
50
|
+
messages: messageResult,
|
51
|
+
sessionGroups: sessionGroupResult,
|
52
|
+
sessions: sessionResult,
|
53
|
+
topics: topicResult,
|
54
|
+
};
|
55
|
+
|
56
|
+
const duration = Date.now() - time;
|
57
|
+
callbacks?.onStageChange?.(ImportStage.Success);
|
58
|
+
callbacks?.onSuccess?.(result, duration);
|
59
|
+
|
60
|
+
return result;
|
61
|
+
};
|
62
|
+
|
63
|
+
private mapImportResult = (input: {
|
64
|
+
added: number;
|
65
|
+
errors?: Error[];
|
66
|
+
skips: string[];
|
67
|
+
}): ImportResult => {
|
68
|
+
return {
|
69
|
+
added: input.added,
|
70
|
+
errors: input.errors?.length || 0,
|
71
|
+
skips: input.skips.length,
|
72
|
+
};
|
73
|
+
};
|
74
|
+
}
|
@@ -15,7 +15,7 @@ import {
|
|
15
15
|
import { CURRENT_CONFIG_VERSION } from '@/migrations';
|
16
16
|
import { ImportResults, ImporterEntryData } from '@/types/importer';
|
17
17
|
|
18
|
-
import { ClientService } from './
|
18
|
+
import { ClientService } from './client';
|
19
19
|
|
20
20
|
const userId = 'test-user-id';
|
21
21
|
const service = new ClientService(userId);
|
@@ -1,74 +1,34 @@
|
|
1
|
-
import {
|
2
|
-
import {
|
3
|
-
import {
|
4
|
-
import { TopicModel } from '@/database/_deprecated/models/topic';
|
5
|
-
import { ImportResult, ImportResults } from '@/services/config';
|
1
|
+
import { clientDB } from '@/database/client/db';
|
2
|
+
import { DataImporterRepos } from '@/database/repositories/dataImporter';
|
3
|
+
import { BaseClientService } from '@/services/baseClientService';
|
6
4
|
import { useUserStore } from '@/store/user';
|
7
5
|
import { ImportStage, ImporterEntryData, OnImportCallbacks } from '@/types/importer';
|
8
6
|
import { UserSettings } from '@/types/user/settings';
|
9
7
|
|
10
|
-
export class ClientService {
|
8
|
+
export class ClientService extends BaseClientService {
|
9
|
+
private get dataImporter(): DataImporterRepos {
|
10
|
+
return new DataImporterRepos(clientDB as any, this.userId);
|
11
|
+
}
|
12
|
+
|
11
13
|
importSettings = async (settings: UserSettings) => {
|
12
14
|
await useUserStore.getState().importAppSettings(settings);
|
13
15
|
};
|
14
16
|
|
15
|
-
importData = async (
|
16
|
-
config: ImporterEntryData,
|
17
|
-
callbacks?: OnImportCallbacks,
|
18
|
-
): Promise<ImportResults> => {
|
17
|
+
importData = async (data: ImporterEntryData, callbacks?: OnImportCallbacks) => {
|
19
18
|
callbacks?.onStageChange?.(ImportStage.Importing);
|
20
19
|
const time = Date.now();
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
const
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
if (sessionGroups.length > 0) {
|
35
|
-
const res = await SessionGroupModel.batchCreate(sessionGroups as any);
|
36
|
-
sessionGroupResult = this.mapImportResult(res);
|
37
|
-
}
|
38
|
-
|
39
|
-
if (topics.length > 0) {
|
40
|
-
const res = await TopicModel.batchCreate(topics as any);
|
41
|
-
topicResult = this.mapImportResult(res);
|
20
|
+
try {
|
21
|
+
const result = await this.dataImporter.importData(data);
|
22
|
+
const duration = Date.now() - time;
|
23
|
+
|
24
|
+
callbacks?.onStageChange?.(ImportStage.Success);
|
25
|
+
callbacks?.onSuccess?.(result, duration);
|
26
|
+
} catch (e) {
|
27
|
+
console.error(e);
|
28
|
+
callbacks?.onStageChange?.(ImportStage.Error);
|
29
|
+
const error = e as Error;
|
30
|
+
|
31
|
+
callbacks?.onError?.({ code: 'ImportError', httpStatus: 0, message: error.message });
|
42
32
|
}
|
43
|
-
|
44
|
-
if (sessions.length > 0) {
|
45
|
-
const data = await SessionModel.batchCreate(sessions as any);
|
46
|
-
sessionResult = this.mapImportResult(data);
|
47
|
-
}
|
48
|
-
|
49
|
-
const result = {
|
50
|
-
messages: messageResult,
|
51
|
-
sessionGroups: sessionGroupResult,
|
52
|
-
sessions: sessionResult,
|
53
|
-
topics: topicResult,
|
54
|
-
};
|
55
|
-
|
56
|
-
const duration = Date.now() - time;
|
57
|
-
callbacks?.onStageChange?.(ImportStage.Success);
|
58
|
-
callbacks?.onSuccess?.(result, duration);
|
59
|
-
|
60
|
-
return result;
|
61
|
-
};
|
62
|
-
|
63
|
-
private mapImportResult = (input: {
|
64
|
-
added: number;
|
65
|
-
errors?: Error[];
|
66
|
-
skips: string[];
|
67
|
-
}): ImportResult => {
|
68
|
-
return {
|
69
|
-
added: input.added,
|
70
|
-
errors: input.errors?.length || 0,
|
71
|
-
skips: input.skips.length,
|
72
|
-
};
|
73
33
|
};
|
74
34
|
}
|
@@ -1,5 +1,5 @@
|
|
1
|
-
import { ClientService as DeprecatedService } from './
|
2
|
-
import { ClientService } from './
|
1
|
+
import { ClientService as DeprecatedService } from './_deprecated';
|
2
|
+
import { ClientService } from './client';
|
3
3
|
import { ServerService } from './server';
|
4
4
|
|
5
5
|
const clientService =
|
@@ -0,0 +1,398 @@
|
|
1
|
+
import dayjs from 'dayjs';
|
2
|
+
import { Mock, describe, expect, it, vi } from 'vitest';
|
3
|
+
|
4
|
+
import { CreateMessageParams, MessageModel } from '@/database/_deprecated/models/message';
|
5
|
+
import {
|
6
|
+
ChatMessage,
|
7
|
+
ChatMessageError,
|
8
|
+
ChatPluginPayload,
|
9
|
+
ChatTTS,
|
10
|
+
ChatTranslate,
|
11
|
+
} from '@/types/message';
|
12
|
+
|
13
|
+
import { ClientService } from './_deprecated';
|
14
|
+
|
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
|
+
};
|
37
|
+
});
|
38
|
+
|
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
|
+
});
|
57
|
+
|
58
|
+
describe('create', () => {
|
59
|
+
it('should create a message and return its id', async () => {
|
60
|
+
// Setup
|
61
|
+
const createParams = {
|
62
|
+
content: 'New message content',
|
63
|
+
sessionId: '1',
|
64
|
+
// ... other properties
|
65
|
+
} as CreateMessageParams;
|
66
|
+
(MessageModel.create as Mock).mockResolvedValue({ id: mockMessageId });
|
67
|
+
|
68
|
+
// Execute
|
69
|
+
const messageId = await messageService.createMessage(createParams);
|
70
|
+
|
71
|
+
// Assert
|
72
|
+
expect(MessageModel.create).toHaveBeenCalledWith(createParams);
|
73
|
+
expect(messageId).toBe(mockMessageId);
|
74
|
+
});
|
75
|
+
});
|
76
|
+
|
77
|
+
describe('batchCreate', () => {
|
78
|
+
it('should batch create messages', async () => {
|
79
|
+
// Setup
|
80
|
+
(MessageModel.batchCreate as Mock).mockResolvedValue(mockMessages);
|
81
|
+
|
82
|
+
// Execute
|
83
|
+
const result = await messageService.batchCreateMessages(mockMessages);
|
84
|
+
|
85
|
+
// Assert
|
86
|
+
expect(MessageModel.batchCreate).toHaveBeenCalledWith(mockMessages);
|
87
|
+
expect(result).toBe(mockMessages);
|
88
|
+
});
|
89
|
+
});
|
90
|
+
|
91
|
+
describe('removeMessage', () => {
|
92
|
+
it('should remove a message by id', async () => {
|
93
|
+
// Setup
|
94
|
+
(MessageModel.delete as Mock).mockResolvedValue(true);
|
95
|
+
|
96
|
+
// Execute
|
97
|
+
const result = await messageService.removeMessage(mockMessageId);
|
98
|
+
|
99
|
+
// Assert
|
100
|
+
expect(MessageModel.delete).toHaveBeenCalledWith(mockMessageId);
|
101
|
+
expect(result).toBe(true);
|
102
|
+
});
|
103
|
+
});
|
104
|
+
describe('removeMessages', () => {
|
105
|
+
it('should remove a message by id', async () => {
|
106
|
+
// Setup
|
107
|
+
(MessageModel.bulkDelete as Mock).mockResolvedValue(true);
|
108
|
+
|
109
|
+
// Execute
|
110
|
+
const result = await messageService.removeMessages([mockMessageId]);
|
111
|
+
|
112
|
+
// Assert
|
113
|
+
expect(MessageModel.bulkDelete).toHaveBeenCalledWith([mockMessageId]);
|
114
|
+
expect(result).toBe(true);
|
115
|
+
});
|
116
|
+
});
|
117
|
+
|
118
|
+
describe('getMessages', () => {
|
119
|
+
it('should retrieve messages by sessionId and topicId', async () => {
|
120
|
+
// Setup
|
121
|
+
const sessionId = 'session-id';
|
122
|
+
const topicId = 'topic-id';
|
123
|
+
(MessageModel.query as Mock).mockResolvedValue(mockMessages);
|
124
|
+
|
125
|
+
// Execute
|
126
|
+
const messages = await messageService.getMessages(sessionId, topicId);
|
127
|
+
|
128
|
+
// Assert
|
129
|
+
expect(MessageModel.query).toHaveBeenCalledWith({ sessionId, topicId });
|
130
|
+
expect(messages).toEqual(mockMessages.map((i) => ({ ...i, imageList: [] })));
|
131
|
+
});
|
132
|
+
});
|
133
|
+
|
134
|
+
describe('getAllMessagesInSession', () => {
|
135
|
+
it('should retrieve all messages in a session', async () => {
|
136
|
+
// Setup
|
137
|
+
const sessionId = 'session-id';
|
138
|
+
(MessageModel.queryBySessionId as Mock).mockResolvedValue(mockMessages);
|
139
|
+
|
140
|
+
// Execute
|
141
|
+
const messages = await messageService.getAllMessagesInSession(sessionId);
|
142
|
+
|
143
|
+
// Assert
|
144
|
+
expect(MessageModel.queryBySessionId).toHaveBeenCalledWith(sessionId);
|
145
|
+
expect(messages).toBe(mockMessages);
|
146
|
+
});
|
147
|
+
});
|
148
|
+
|
149
|
+
describe('removeMessagesByAssistant', () => {
|
150
|
+
it('should batch remove messages by assistantId and topicId', async () => {
|
151
|
+
// Setup
|
152
|
+
const assistantId = 'assistant-id';
|
153
|
+
const topicId = 'topic-id';
|
154
|
+
(MessageModel.batchDelete as Mock).mockResolvedValue(true);
|
155
|
+
|
156
|
+
// Execute
|
157
|
+
const result = await messageService.removeMessagesByAssistant(assistantId, topicId);
|
158
|
+
|
159
|
+
// Assert
|
160
|
+
expect(MessageModel.batchDelete).toHaveBeenCalledWith(assistantId, topicId);
|
161
|
+
expect(result).toBe(true);
|
162
|
+
});
|
163
|
+
});
|
164
|
+
|
165
|
+
describe('clearAllMessage', () => {
|
166
|
+
it('should clear all messages from the table', async () => {
|
167
|
+
// Setup
|
168
|
+
(MessageModel.clearTable as Mock).mockResolvedValue(true);
|
169
|
+
|
170
|
+
// Execute
|
171
|
+
const result = await messageService.removeAllMessages();
|
172
|
+
|
173
|
+
// 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);
|
192
|
+
});
|
193
|
+
});
|
194
|
+
|
195
|
+
describe('getAllMessages', () => {
|
196
|
+
it('should retrieve all messages', async () => {
|
197
|
+
// Setup
|
198
|
+
(MessageModel.queryAll as Mock).mockResolvedValue(mockMessages);
|
199
|
+
|
200
|
+
// Execute
|
201
|
+
const messages = await messageService.getAllMessages();
|
202
|
+
|
203
|
+
// Assert
|
204
|
+
expect(MessageModel.queryAll).toHaveBeenCalled();
|
205
|
+
expect(messages).toBe(mockMessages);
|
206
|
+
});
|
207
|
+
});
|
208
|
+
|
209
|
+
describe('updateMessageError', () => {
|
210
|
+
it('should update the error field of a message', async () => {
|
211
|
+
// Setup
|
212
|
+
const newError = {
|
213
|
+
type: 'InvalidProviderAPIKey',
|
214
|
+
message: 'Error occurred',
|
215
|
+
} as ChatMessageError;
|
216
|
+
(MessageModel.update as Mock).mockResolvedValue({ ...mockMessage, error: newError });
|
217
|
+
|
218
|
+
// Execute
|
219
|
+
const result = await messageService.updateMessageError(mockMessageId, newError);
|
220
|
+
|
221
|
+
// Assert
|
222
|
+
expect(MessageModel.update).toHaveBeenCalledWith(mockMessageId, { error: newError });
|
223
|
+
expect(result).toEqual({ ...mockMessage, error: newError });
|
224
|
+
});
|
225
|
+
});
|
226
|
+
|
227
|
+
// describe('updateMessagePlugin', () => {
|
228
|
+
// it('should update the plugin payload of a message', async () => {
|
229
|
+
// // Setup
|
230
|
+
// const newPlugin = {
|
231
|
+
// type: 'default',
|
232
|
+
// apiName: 'abc',
|
233
|
+
// arguments: '',
|
234
|
+
// identifier: 'plugin1',
|
235
|
+
// } as ChatPluginPayload;
|
236
|
+
//
|
237
|
+
// (MessageModel.update as Mock).mockResolvedValue({ ...mockMessage, plugin: newPlugin });
|
238
|
+
//
|
239
|
+
// // Execute
|
240
|
+
// const result = await messageService.updateMessagePlugin(mockMessageId, newPlugin);
|
241
|
+
//
|
242
|
+
// // Assert
|
243
|
+
// expect(MessageModel.update).toHaveBeenCalledWith(mockMessageId, { plugin: newPlugin });
|
244
|
+
// expect(result).toEqual({ ...mockMessage, plugin: newPlugin });
|
245
|
+
// });
|
246
|
+
// });
|
247
|
+
|
248
|
+
describe('updateMessagePluginState', () => {
|
249
|
+
it('should update the plugin state of a message', async () => {
|
250
|
+
// Setup
|
251
|
+
const key = 'stateKey';
|
252
|
+
const value = 'stateValue';
|
253
|
+
const newPluginState = { [key]: value };
|
254
|
+
(MessageModel.updatePluginState as Mock).mockResolvedValue({
|
255
|
+
...mockMessage,
|
256
|
+
pluginState: newPluginState,
|
257
|
+
});
|
258
|
+
|
259
|
+
// Execute
|
260
|
+
const result = await messageService.updateMessagePluginState(mockMessageId, { key: value });
|
261
|
+
|
262
|
+
// Assert
|
263
|
+
expect(MessageModel.updatePluginState).toHaveBeenCalledWith(mockMessageId, { key: value });
|
264
|
+
expect(result).toEqual({ ...mockMessage, pluginState: newPluginState });
|
265
|
+
});
|
266
|
+
});
|
267
|
+
|
268
|
+
describe('updateMessagePluginArguments', () => {
|
269
|
+
it('should update the plugin arguments object of a message', async () => {
|
270
|
+
// Setup
|
271
|
+
const key = 'stateKey';
|
272
|
+
const value = 'stateValue';
|
273
|
+
(MessageModel.updatePlugin as Mock).mockResolvedValue({});
|
274
|
+
|
275
|
+
// Execute
|
276
|
+
await messageService.updateMessagePluginArguments(mockMessageId, { key: value });
|
277
|
+
|
278
|
+
// Assert
|
279
|
+
expect(MessageModel.updatePlugin).toHaveBeenCalledWith(mockMessageId, {
|
280
|
+
arguments: '{"key":"stateValue"}',
|
281
|
+
});
|
282
|
+
});
|
283
|
+
it('should update the plugin arguments string of a message', async () => {
|
284
|
+
// Setup
|
285
|
+
const key = 'stateKey';
|
286
|
+
const value = 'stateValue';
|
287
|
+
(MessageModel.updatePlugin as Mock).mockResolvedValue({});
|
288
|
+
|
289
|
+
// Execute
|
290
|
+
await messageService.updateMessagePluginArguments(
|
291
|
+
mockMessageId,
|
292
|
+
JSON.stringify({ key: value }),
|
293
|
+
);
|
294
|
+
|
295
|
+
// Assert
|
296
|
+
expect(MessageModel.updatePlugin).toHaveBeenCalledWith(mockMessageId, {
|
297
|
+
arguments: '{"key":"stateValue"}',
|
298
|
+
});
|
299
|
+
});
|
300
|
+
});
|
301
|
+
|
302
|
+
describe('countMessages', () => {
|
303
|
+
it('should count the total number of messages', async () => {
|
304
|
+
// Setup
|
305
|
+
const mockCount = 10;
|
306
|
+
(MessageModel.count as Mock).mockResolvedValue(mockCount);
|
307
|
+
|
308
|
+
// Execute
|
309
|
+
const count = await messageService.countMessages();
|
310
|
+
|
311
|
+
// Assert
|
312
|
+
expect(MessageModel.count).toHaveBeenCalled();
|
313
|
+
expect(count).toBe(mockCount);
|
314
|
+
});
|
315
|
+
});
|
316
|
+
|
317
|
+
describe('countTodayMessages', () => {
|
318
|
+
it('should count the number of messages created today', async () => {
|
319
|
+
// Setup
|
320
|
+
const today = dayjs().format('YYYY-MM-DD');
|
321
|
+
const mockMessages = [
|
322
|
+
{ ...mockMessage, createdAt: today },
|
323
|
+
{ ...mockMessage, createdAt: today },
|
324
|
+
{ ...mockMessage, createdAt: '2023-01-01' },
|
325
|
+
];
|
326
|
+
(MessageModel.queryAll as Mock).mockResolvedValue(mockMessages);
|
327
|
+
|
328
|
+
// Execute
|
329
|
+
const count = await messageService.countTodayMessages();
|
330
|
+
|
331
|
+
// Assert
|
332
|
+
expect(MessageModel.queryAll).toHaveBeenCalled();
|
333
|
+
expect(count).toBe(2);
|
334
|
+
});
|
335
|
+
});
|
336
|
+
|
337
|
+
describe('updateMessageTTS', () => {
|
338
|
+
it('should update the TTS field of a message', async () => {
|
339
|
+
// Setup
|
340
|
+
const newTTS: ChatTTS = {
|
341
|
+
contentMd5: 'abc',
|
342
|
+
file: 'file-abc',
|
343
|
+
};
|
344
|
+
|
345
|
+
(MessageModel.update as Mock).mockResolvedValue({ ...mockMessage, tts: newTTS });
|
346
|
+
|
347
|
+
// Execute
|
348
|
+
const result = await messageService.updateMessageTTS(mockMessageId, newTTS);
|
349
|
+
|
350
|
+
// Assert
|
351
|
+
expect(MessageModel.update).toHaveBeenCalledWith(mockMessageId, { tts: newTTS });
|
352
|
+
expect(result).toEqual({ ...mockMessage, tts: newTTS });
|
353
|
+
});
|
354
|
+
});
|
355
|
+
|
356
|
+
describe('updateMessageTranslate', () => {
|
357
|
+
it('should update the translate field of a message', async () => {
|
358
|
+
// Setup
|
359
|
+
const newTranslate: ChatTranslate = {
|
360
|
+
content: 'Translated text',
|
361
|
+
to: 'es',
|
362
|
+
};
|
363
|
+
|
364
|
+
(MessageModel.update as Mock).mockResolvedValue({ ...mockMessage, translate: newTranslate });
|
365
|
+
|
366
|
+
// Execute
|
367
|
+
const result = await messageService.updateMessageTranslate(mockMessageId, newTranslate);
|
368
|
+
|
369
|
+
// Assert
|
370
|
+
expect(MessageModel.update).toHaveBeenCalledWith(mockMessageId, { translate: newTranslate });
|
371
|
+
expect(result).toEqual({ ...mockMessage, translate: newTranslate });
|
372
|
+
});
|
373
|
+
});
|
374
|
+
|
375
|
+
describe('hasMessages', () => {
|
376
|
+
it('should return true if there are messages', async () => {
|
377
|
+
// Setup
|
378
|
+
(MessageModel.count as Mock).mockResolvedValue(1);
|
379
|
+
|
380
|
+
// Execute
|
381
|
+
const result = await messageService.hasMessages();
|
382
|
+
|
383
|
+
// Assert
|
384
|
+
expect(result).toBe(true);
|
385
|
+
});
|
386
|
+
|
387
|
+
it('should return false if there are no messages', async () => {
|
388
|
+
// Setup
|
389
|
+
(MessageModel.count as Mock).mockResolvedValue(0);
|
390
|
+
|
391
|
+
// Execute
|
392
|
+
const result = await messageService.hasMessages();
|
393
|
+
|
394
|
+
// Assert
|
395
|
+
expect(result).toBe(false);
|
396
|
+
});
|
397
|
+
});
|
398
|
+
});
|