@lobehub/chat
Version:
Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.
428 lines (365 loc) • 13.7 kB
text/typescript
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { ChatMessage } from '@/types/message';
import { CreateMessageParams, MessageModel } from '../message';
describe('MessageModel', () => {
let messageData: CreateMessageParams;
beforeEach(() => {
// 设置正确结构的消息数据
messageData = {
content: 'Test message content',
role: 'user',
sessionId: 'session1',
topicId: 'topic1',
};
});
afterEach(async () => {
// 每次测试后清理数据库
await MessageModel.clearTable();
});
describe('create', () => {
it('should create a message record', async () => {
const result = await MessageModel.create(messageData);
expect(result).toHaveProperty('id');
// 验证消息是否已添加到数据库
const messageInDb = await MessageModel.findById(result.id);
expect(messageInDb).toEqual(
expect.objectContaining({
content: messageData.content,
role: messageData.role,
sessionId: messageData.sessionId,
topicId: messageData.topicId,
}),
);
});
it('should create with tts', async () => {
const result = await MessageModel.create({
content: 'abc',
role: 'assistant',
extra: { translate: { content: 'avc', from: 'a', to: 'f' } },
sessionId: 'a',
});
// 验证消息是否已添加到数据库
const messageInDb = await MessageModel.findById(result.id);
expect(messageInDb).toEqual(
expect.objectContaining({
content: 'abc',
role: 'assistant',
translate: { content: 'avc', from: 'a', to: 'f' },
sessionId: 'a',
}),
);
});
});
describe('batchCreate', () => {
it('should batch create message records', async () => {
const messagesToCreate = [messageData, messageData] as ChatMessage[];
const results = await MessageModel.batchCreate(messagesToCreate);
expect(results.success).toBeTruthy();
expect(results.errors).toBeUndefined();
// 验证消息是否已添加到数据库
for (const message of results.ids!) {
const messageInDb = await MessageModel.findById(message);
expect(messageInDb).toEqual(
expect.objectContaining({
content: messageData.content,
role: messageData.role,
sessionId: messageData.sessionId,
topicId: messageData.topicId,
}),
);
}
});
});
describe('query', () => {
it('should query messages with pagination', async () => {
// 创建多条消息以测试查询方法
await MessageModel.batchCreate([messageData, messageData] as ChatMessage[]);
const queriedMessages = await MessageModel.query({
pageSize: 1,
current: 0,
sessionId: messageData.sessionId,
topicId: messageData.topicId,
});
expect(queriedMessages).toHaveLength(1);
});
it('should query correctly without topic id', async () => {
// 创建多条消息以测试查询方法
await MessageModel.batchCreate([messageData, messageData] as ChatMessage[]);
const queriedMessages = await MessageModel.query({ sessionId: messageData.sessionId });
expect(queriedMessages).toHaveLength(0);
});
it('should query correctly with exactly topic id', async () => {
// 创建多条消息以测试查询方法
await MessageModel.batchCreate([
messageData,
{ ...messageData, topicId: undefined },
] as ChatMessage[]);
const queriedMessages = await MessageModel.query({ sessionId: messageData.sessionId });
expect(queriedMessages).toHaveLength(1);
});
it('should should have correct order', async () => {
const data: ChatMessage[] = [
{
role: 'user',
content: '1',
createdAt: 1697120044345,
id: 'NQ7RscYx',
updatedAt: 1697120181827,
extra: {},
meta: {},
sessionId: '1',
},
{
role: 'assistant',
content: '2',
parentId: 'NQ7RscYx',
createdAt: 1697120130973,
id: '9tDAumEx',
updatedAt: 1697120181827,
meta: {},
extra: {
fromModel: 'gpt-3.5-turbo-16k',
},
sessionId: '1',
},
{
role: 'assistant',
content: '3',
parentId: 'tOMH7c5R',
meta: {},
createdAt: 1697120163272,
id: '5Ie5hClg',
updatedAt: 1697120181827,
extra: {
fromModel: 'gpt-3.5-turbo-16k',
},
sessionId: '1',
},
{
role: 'user',
content: '4',
meta: {},
createdAt: 1697120163272,
id: 'tOMH7c5R',
updatedAt: 1697120181827,
extra: {},
sessionId: '1',
},
];
await MessageModel.batchCreate(data);
const queriedMessages = await MessageModel.query({ sessionId: '1' });
expect(queriedMessages).toEqual([
{
role: 'user',
content: '1',
createdAt: 1697120044345,
id: 'NQ7RscYx',
updatedAt: 1697120181827,
sessionId: '1',
extra: {},
meta: {},
},
{
role: 'assistant',
content: '2',
parentId: 'NQ7RscYx',
createdAt: 1697120130973,
id: '9tDAumEx',
sessionId: '1',
updatedAt: 1697120181827,
meta: {},
extra: {
fromModel: 'gpt-3.5-turbo-16k',
},
},
{
role: 'user',
content: '4',
sessionId: '1',
createdAt: 1697120163272,
id: 'tOMH7c5R',
updatedAt: 1697120181827,
meta: {},
extra: {},
},
{
role: 'assistant',
content: '3',
parentId: 'tOMH7c5R',
meta: {},
createdAt: 1697120163272,
sessionId: '1',
id: '5Ie5hClg',
updatedAt: 1697120181827,
extra: {
fromModel: 'gpt-3.5-turbo-16k',
},
},
]);
});
});
describe('findById', () => {
it('should find a message by id', async () => {
const createdMessage = await MessageModel.create(messageData);
const messageInDb = await MessageModel.findById(createdMessage.id);
expect(messageInDb).toEqual(
expect.objectContaining({
id: createdMessage.id,
content: messageData.content,
}),
);
});
});
describe('delete', () => {
it('should delete a message', async () => {
const createdMessage = await MessageModel.create(messageData);
await MessageModel.delete(createdMessage.id);
const messageInDb = await MessageModel.findById(createdMessage.id);
expect(messageInDb).toBeUndefined();
});
});
describe('bulkDelete', () => {
it('should delete many messages', async () => {
const createdMessage = await MessageModel.create(messageData);
const createdMessage2 = await MessageModel.create(messageData);
await MessageModel.bulkDelete([createdMessage.id, createdMessage2.id]);
const messageInDb1 = await MessageModel.findById(createdMessage.id);
const messageInDb2 = await MessageModel.findById(createdMessage2.id);
expect(messageInDb1).toBeUndefined();
expect(messageInDb2).toBeUndefined();
});
});
describe('update', () => {
it('should update a message', async () => {
const createdMessage = await MessageModel.create(messageData);
const updateData = { content: 'Updated content' };
await MessageModel.update(createdMessage.id, updateData);
const updatedMessage = await MessageModel.findById(createdMessage.id);
expect(updatedMessage).toHaveProperty('content', 'Updated content');
});
it('should update a role and plugins', async () => {
const createdMessage = await MessageModel.create(messageData);
const updateData = {
role: 'tool' as const,
plugin: { apiName: 'a', identifier: 'b', arguments: 'abc' },
};
await MessageModel.update(createdMessage.id, updateData);
const updatedMessage = await MessageModel.findById(createdMessage.id);
expect(updatedMessage).toHaveProperty('role', 'tool');
});
});
describe('batchUpdate', () => {
it('should batch update messages', async () => {
const createdMessage1 = await MessageModel.create(messageData);
const createdMessage2 = await MessageModel.create(messageData);
const updateData = { content: 'Batch updated content' };
const numUpdated = await MessageModel.batchUpdate(
[createdMessage1.id, createdMessage2.id],
updateData,
);
expect(numUpdated).toBe(2);
const updatedMessage1 = await MessageModel.findById(createdMessage1.id);
const updatedMessage2 = await MessageModel.findById(createdMessage2.id);
expect(updatedMessage1).toHaveProperty('content', 'Batch updated content');
expect(updatedMessage2).toHaveProperty('content', 'Batch updated content');
});
});
describe('batchDelete', () => {
it('should batch delete messages by session id', async () => {
// 创建多条消息以测试批量删除方法
const createdMessage1 = await MessageModel.create(messageData);
const createdMessage2 = await MessageModel.create(messageData);
await MessageModel.batchDelete(messageData.sessionId, undefined);
// 验证所有具有给定会话 ID 的消息是否已删除
const messagesInDb = await MessageModel.query({ sessionId: messageData.sessionId });
expect(messagesInDb).toHaveLength(0);
});
it('should batch delete messages by session id and topic id', async () => {
// 创建多条消息以测试批量删除方法
const createdMessage1 = await MessageModel.create(messageData);
const createdMessage2 = await MessageModel.create(messageData);
await MessageModel.batchDelete(messageData.sessionId, messageData.topicId);
// 验证所有具有给定会话 ID 和话题 ID 的消息是否已删除
const messagesInDb = await MessageModel.query({
sessionId: messageData.sessionId,
topicId: messageData.topicId,
});
expect(messagesInDb).toHaveLength(0);
});
});
describe('duplicateMessages', () => {
it('should duplicate messages and update parentId for copied messages', async () => {
// 创建原始消息和父消息
const parentMessageData: CreateMessageParams = {
content: 'Parent message content',
role: 'user',
sessionId: 'session1',
topicId: undefined,
};
const parentMessage = await MessageModel.create(parentMessageData);
const childMessageData: CreateMessageParams = {
content: 'Child message content',
role: 'user',
sessionId: 'session1',
parentId: parentMessage.id,
};
await MessageModel.create(childMessageData);
// 获取数据库中的消息以进行复制
const originalMessages = await MessageModel.queryAll();
// 执行复制操作
const duplicatedMessages = await MessageModel.duplicateMessages(originalMessages);
// 验证复制的消息数量是否正确
expect(duplicatedMessages.length).toBe(originalMessages.length);
// 验证每个复制的消息是否具有新的唯一ID,并且parentId被正确更新
for (const original of originalMessages) {
const copied = duplicatedMessages.find((m) => m.content === original.content);
expect(copied).toBeDefined();
expect(copied).not.toBeNull();
expect(copied!.id).not.toBe(original.id);
if (original.parentId) {
const originalParent = originalMessages.find((m) => m.id === original.parentId);
expect(originalParent).toBeDefined();
const copiedParent = duplicatedMessages.find(
(m) => m.content === originalParent!.content,
);
expect(copied!.parentId).toBe(copiedParent!.id);
}
}
});
});
describe('clearTable', () => {
it('should clear the table', async () => {
await MessageModel.create(messageData);
await MessageModel.clearTable();
const messages = await MessageModel.queryAll();
expect(messages).toHaveLength(0);
});
});
describe('updatePluginState', () => {
it('should update plugin state', async () => {
const createdMessage = await MessageModel.create(messageData);
await MessageModel.updatePluginState(createdMessage.id, { testKey: 'testValue' });
const updatedMessage = await MessageModel.findById(createdMessage.id);
expect(updatedMessage.pluginState).toHaveProperty('testKey', 'testValue');
});
});
describe('updatePlugin', () => {
it('should update plugin', async () => {
const value = {
identifier: 'testValue',
arguments: 'abc',
apiName: 'abc',
};
const createdMessage = await MessageModel.create(messageData);
await MessageModel.updatePlugin(createdMessage.id, value);
const updatedMessage = await MessageModel.findById(createdMessage.id);
expect(updatedMessage.plugin).toEqual(value);
});
});
describe('isEmpty', () => {
it('should return true if table is empty', async () => {
const number = await MessageModel.count();
expect(number === 0).toBeTruthy();
});
});
});