@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.
331 lines (273 loc) • 10.3 kB
text/typescript
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { clientDB, initializeDB } from '@/database/client/db';
import {
agents,
agentsKnowledgeBases,
agentsToSessions,
files,
filesToSessions,
globalFiles,
knowledgeBaseFiles,
knowledgeBases,
messages,
sessionGroups,
sessions,
topics,
userSettings,
users,
} from '@/database/schemas';
import { LobeChatDatabase } from '@/database/type';
import { DATA_EXPORT_CONFIG, DataExporterRepos } from './index';
let db = clientDB as LobeChatDatabase;
// 设置测试数据
describe('DataExporterRepos', () => {
// 测试数据 ID
const testIds = {
userId: 'test-user-id',
fileId: 'test-file-id',
fileHash: 'test-file-hash',
sessionId: 'test-session-id',
agentId: 'test-agent-id',
topicId: 'test-topic-id',
messageId: 'test-message-id',
knowledgeBaseId: 'test-kb-id',
};
// 设置测试环境
let userId: string = testIds.userId;
const setupTestData = async () => {
await db.transaction(async (trx) => {
// 用户数据
await trx.insert(users).values({
id: testIds.userId,
username: 'testuser',
email: 'test@example.com',
});
// 用户设置
await trx.insert(userSettings).values({
id: testIds.userId,
general: { theme: 'light' },
});
// 全局文件
await trx.insert(globalFiles).values({
hashId: testIds.fileHash,
fileType: 'text/plain',
size: 1024,
url: 'https://example.com/test-file.txt',
creator: testIds.userId,
});
// 文件数据
await trx.insert(files).values({
id: testIds.fileId,
userId: testIds.userId,
fileType: 'text/plain',
fileHash: testIds.fileHash,
name: 'test-file.txt',
size: 1024,
url: 'https://example.com/test-file.txt',
});
// 会话组
await trx.insert(sessionGroups).values({
name: 'Test Group',
userId: testIds.userId,
});
// 会话
await trx.insert(sessions).values({
id: testIds.sessionId,
slug: 'test-session',
title: 'Test Session',
userId: testIds.userId,
});
// 主题
await trx.insert(topics).values({
id: testIds.topicId,
title: 'Test Topic',
sessionId: testIds.sessionId,
userId: testIds.userId,
});
// 消息
await trx.insert(messages).values({
id: testIds.messageId,
role: 'user',
content: 'Hello, world!',
userId: testIds.userId,
sessionId: testIds.sessionId,
topicId: testIds.topicId,
});
// 代理
await trx.insert(agents).values({
id: testIds.agentId,
title: 'Test Agent',
userId: testIds.userId,
});
// 代理到会话的关联
await trx.insert(agentsToSessions).values({
agentId: testIds.agentId,
sessionId: testIds.sessionId,
userId: testIds.userId,
});
// 文件到会话的关联
await trx.insert(filesToSessions).values({
fileId: testIds.fileId,
sessionId: testIds.sessionId,
userId: testIds.userId,
});
// 知识库
await trx.insert(knowledgeBases).values({
id: testIds.knowledgeBaseId,
name: 'Test Knowledge Base',
userId: testIds.userId,
});
// 知识库文件
await trx.insert(knowledgeBaseFiles).values({
knowledgeBaseId: testIds.knowledgeBaseId,
fileId: testIds.fileId,
userId: testIds.userId,
});
// 代理知识库
await trx.insert(agentsKnowledgeBases).values({
agentId: testIds.agentId,
knowledgeBaseId: testIds.knowledgeBaseId,
userId: testIds.userId,
});
});
};
beforeEach(async () => {
// 创建内存数据库
await initializeDB();
// 插入测试数据
await setupTestData();
});
afterEach(async () => {
await db.delete(users);
await db.delete(globalFiles);
vi.restoreAllMocks();
});
describe('export', () => {
it('should export all user data correctly', async () => {
// 创建导出器实例
const dataExporter = new DataExporterRepos(db, userId);
// 执行导出
const result = await dataExporter.export();
// 验证基础表导出结果
// expect(result).toHaveProperty('users');
// expect(result.users).toHaveLength(1);
// expect(result.users[0]).toHaveProperty('id', testIds.userId);
// expect(result.users[0]).not.toHaveProperty('userId'); // userId 字段应该被移除
expect(result).toHaveProperty('userSettings');
expect(result.userSettings).toHaveLength(1);
expect(result.userSettings[0]).toHaveProperty('id', testIds.userId);
// expect(result).toHaveProperty('files');
// expect(result.files).toHaveLength(1);
// expect(result.files[0]).toHaveProperty('id', testIds.fileId);
// expect(result.files[0]).toHaveProperty('fileHash', testIds.fileHash);
// expect(result.files[0]).not.toHaveProperty('userId');
expect(result).toHaveProperty('sessions');
expect(result.sessions).toHaveLength(1);
expect(result.sessions[0]).toHaveProperty('id', testIds.sessionId);
expect(result).toHaveProperty('topics');
expect(result.topics).toHaveLength(1);
expect(result.topics[0]).toHaveProperty('id', testIds.topicId);
expect(result).toHaveProperty('messages');
expect(result.messages).toHaveLength(1);
expect(result.messages[0]).toHaveProperty('id', testIds.messageId);
expect(result).toHaveProperty('agents');
expect(result.agents).toHaveLength(1);
expect(result.agents[0]).toHaveProperty('id', testIds.agentId);
// expect(result).toHaveProperty('knowledgeBases');
// expect(result.knowledgeBases).toHaveLength(1);
// expect(result.knowledgeBases[0]).toHaveProperty('id', testIds.knowledgeBaseId);
// 验证关联表导出结果
// expect(result).toHaveProperty('globalFiles');
// expect(result.globalFiles).toHaveLength(1);
// expect(result.globalFiles[0]).toHaveProperty('hashId', testIds.fileHash);
expect(result).toHaveProperty('agentsToSessions');
expect(result.agentsToSessions).toHaveLength(1);
expect(result.agentsToSessions[0]).toHaveProperty('agentId', testIds.agentId);
expect(result.agentsToSessions[0]).toHaveProperty('sessionId', testIds.sessionId);
// expect(result).toHaveProperty('filesToSessions');
// expect(result.filesToSessions).toHaveLength(1);
// expect(result.filesToSessions[0]).toHaveProperty('fileId', testIds.fileId);
// expect(result.filesToSessions[0]).toHaveProperty('sessionId', testIds.sessionId);
// expect(result).toHaveProperty('knowledgeBaseFiles');
// expect(result.knowledgeBaseFiles).toHaveLength(1);
// expect(result.knowledgeBaseFiles[0]).toHaveProperty(
// 'knowledgeBaseId',
// testIds.knowledgeBaseId,
// );
// expect(result.knowledgeBaseFiles[0]).toHaveProperty('fileId', testIds.fileId);
});
it('should handle empty database gracefully', async () => {
// 清空数据库
await db.delete(users);
await db.delete(globalFiles);
// 创建导出器实例
const dataExporter = new DataExporterRepos(db, userId);
// 执行导出
const result = await dataExporter.export();
// 验证所有表都返回空数组
DATA_EXPORT_CONFIG.baseTables.forEach(({ table }) => {
expect(result).toHaveProperty(table);
expect(result[table]).toEqual([]);
});
DATA_EXPORT_CONFIG.relationTables.forEach(({ table }) => {
expect(result).toHaveProperty(table);
expect(result[table]).toEqual([]);
});
});
it('should handle database query errors', async () => {
// 模拟查询错误
// @ts-ignore
vi.spyOn(db.query.users, 'findMany').mockRejectedValueOnce(new Error('Database error'));
// 创建导出器实例
const dataExporter = new DataExporterRepos(db, userId);
// 执行导出
const result = await dataExporter.export();
// 验证其他表仍然被导出
expect(result).toHaveProperty('sessions');
expect(result.sessions).toHaveLength(1);
});
it.skip('should skip relation tables when source tables have no data', async () => {
// 删除文件数据,这将导致 globalFiles 表被跳过
await db.delete(files);
// 创建导出器实例
const dataExporter = new DataExporterRepos(db, userId);
// 执行导出
const result = await dataExporter.export();
// 验证文件表为空
// expect(result).toHaveProperty('files');
// expect(result.files).toEqual([]);
// 验证关联表也为空
// expect(result).toHaveProperty('globalFiles');
// expect(result.globalFiles).toEqual([]);
});
it('should export data for a different user', async () => {
// 创建另一个用户
const anotherUserId = 'another-user-id';
await db.transaction(async (trx) => {
await trx.insert(users).values({
id: anotherUserId,
username: 'anotheruser',
email: 'another@example.com',
});
await trx.insert(sessions).values({
id: 'another-session-id',
slug: 'another-session',
title: 'Another Session',
userId: anotherUserId,
});
});
// 创建导出器实例,使用另一个用户 ID
const dataExporter = new DataExporterRepos(db, anotherUserId);
// 执行导出
const result = await dataExporter.export();
// 验证只导出了另一个用户的数据
// expect(result).toHaveProperty('users');
// expect(result.users).toHaveLength(1);
// expect(result.users[0]).toHaveProperty('id', anotherUserId);
expect(result).toHaveProperty('sessions');
expect(result.sessions).toHaveLength(1);
expect(result.sessions[0]).not.toHaveProperty('userId', anotherUserId);
expect(result.sessions[0]).toHaveProperty('id', 'another-session-id');
});
});
});