UNPKG

@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.

225 lines (178 loc) 7.45 kB
import { beforeEach, describe, expect, it, vi } from 'vitest'; import { S3StaticFileImpl } from './s3'; const config = { S3_ENABLE_PATH_STYLE: false, S3_PUBLIC_DOMAIN: 'https://example.com', S3_BUCKET: 'my-bucket', S3_SET_ACL: true, }; // 模拟 fileEnv vi.mock('@/config/file', () => ({ get fileEnv() { return config; }, })); // 模拟 S3 类 vi.mock('@/server/modules/S3', () => ({ S3: vi.fn().mockImplementation(() => ({ createPreSignedUrlForPreview: vi .fn() .mockResolvedValue('https://presigned.example.com/test.jpg'), getFileContent: vi.fn().mockResolvedValue('file content'), getFileByteArray: vi.fn().mockResolvedValue(new Uint8Array([1, 2, 3])), deleteFile: vi.fn().mockResolvedValue({}), deleteFiles: vi.fn().mockResolvedValue({}), createPreSignedUrl: vi.fn().mockResolvedValue('https://upload.example.com/test.jpg'), uploadContent: vi.fn().mockResolvedValue({}), uploadMedia: vi.fn().mockResolvedValue({}), })), })); describe('S3StaticFileImpl', () => { let fileService: S3StaticFileImpl; beforeEach(() => { fileService = new S3StaticFileImpl(); }); describe('getFullFileUrl', () => { it('should return empty string for null or undefined input', async () => { expect(await fileService.getFullFileUrl(null)).toBe(''); expect(await fileService.getFullFileUrl(undefined)).toBe(''); }); it('当S3_SET_ACL为false时应返回预签名URL', async () => { config.S3_SET_ACL = false; const url = 'path/to/file.jpg'; expect(await fileService.getFullFileUrl(url)).toBe('https://presigned.example.com/test.jpg'); config.S3_SET_ACL = true; }); it('should return correct URL when S3_ENABLE_PATH_STYLE is false', async () => { const url = 'path/to/file.jpg'; expect(await fileService.getFullFileUrl(url)).toBe('https://example.com/path/to/file.jpg'); }); it('should return correct URL when S3_ENABLE_PATH_STYLE is true', async () => { config.S3_ENABLE_PATH_STYLE = true; const url = 'path/to/file.jpg'; expect(await fileService.getFullFileUrl(url)).toBe( 'https://example.com/my-bucket/path/to/file.jpg', ); config.S3_ENABLE_PATH_STYLE = false; }); }); describe('getFileContent', () => { it('应该返回文件内容', async () => { expect(await fileService.getFileContent('test.txt')).toBe('file content'); }); }); describe('getFileByteArray', () => { it('应该返回文件字节数组', async () => { const result = await fileService.getFileByteArray('test.jpg'); expect(result).toBeInstanceOf(Uint8Array); expect(result.length).toBe(3); }); }); describe('deleteFile', () => { it('应该调用S3的deleteFile方法', async () => { await fileService.deleteFile('test.jpg'); expect(fileService['s3'].deleteFile).toHaveBeenCalledWith('test.jpg'); }); }); describe('deleteFiles', () => { it('应该调用S3的deleteFiles方法', async () => { await fileService.deleteFiles(['test1.jpg', 'test2.jpg']); expect(fileService['s3'].deleteFiles).toHaveBeenCalledWith(['test1.jpg', 'test2.jpg']); }); }); describe('createPreSignedUrl', () => { it('应该调用S3的createPreSignedUrl方法', async () => { const result = await fileService.createPreSignedUrl('test.jpg'); expect(result).toBe('https://upload.example.com/test.jpg'); }); }); describe('uploadContent', () => { it('应该调用S3的uploadContent方法', async () => { await fileService.uploadContent('test.jpg', 'content'); expect(fileService['s3'].uploadContent).toHaveBeenCalledWith('test.jpg', 'content'); }); }); describe('getKeyFromFullUrl', () => { it('当S3_ENABLE_PATH_STYLE为false时应该正确提取key', () => { config.S3_ENABLE_PATH_STYLE = false; const url = 'https://example.com/path/to/file.jpg'; const result = fileService.getKeyFromFullUrl(url); expect(result).toBe('path/to/file.jpg'); config.S3_ENABLE_PATH_STYLE = false; // reset }); it('当S3_ENABLE_PATH_STYLE为true时应该正确提取key', () => { config.S3_ENABLE_PATH_STYLE = true; const url = 'https://example.com/my-bucket/path/to/file.jpg'; const result = fileService.getKeyFromFullUrl(url); expect(result).toBe('path/to/file.jpg'); config.S3_ENABLE_PATH_STYLE = false; // reset }); it('当S3_ENABLE_PATH_STYLE为true但缺少bucket名称时应该返回pathname', () => { config.S3_ENABLE_PATH_STYLE = true; config.S3_BUCKET = ''; const url = 'https://example.com/path/to/file.jpg'; const result = fileService.getKeyFromFullUrl(url); expect(result).toBe('path/to/file.jpg'); config.S3_ENABLE_PATH_STYLE = false; // reset config.S3_BUCKET = 'my-bucket'; // reset }); it('当URL格式不正确时应该返回原始字符串', () => { const invalidUrl = 'not-a-valid-url'; const result = fileService.getKeyFromFullUrl(invalidUrl); expect(result).toBe('not-a-valid-url'); }); it('应该处理根路径文件', () => { config.S3_ENABLE_PATH_STYLE = false; const url = 'https://example.com/file.jpg'; const result = fileService.getKeyFromFullUrl(url); expect(result).toBe('file.jpg'); }); it('当path-style URL路径格式不符合预期时应该使用fallback', () => { config.S3_ENABLE_PATH_STYLE = true; const url = 'https://example.com/unexpected/path/file.jpg'; const result = fileService.getKeyFromFullUrl(url); expect(result).toBe('unexpected/path/file.jpg'); config.S3_ENABLE_PATH_STYLE = false; // reset }); }); describe('uploadMedia', () => { beforeEach(() => { // 重置 S3 mock vi.clearAllMocks(); }); it('应该调用S3的uploadMedia方法并返回key', async () => { // 准备 const testKey = 'images/test.jpg'; const testBuffer = Buffer.from('fake image data'); fileService['s3'].uploadMedia = vi.fn().mockResolvedValue(undefined); // 执行 const result = await fileService.uploadMedia(testKey, testBuffer); // 验证 expect(fileService['s3'].uploadMedia).toHaveBeenCalledWith(testKey, testBuffer); expect(result).toEqual({ key: testKey }); }); it('应该正确处理不同类型的媒体文件', async () => { // 准备 const testKey = 'videos/test.mp4'; const testBuffer = Buffer.from('fake video data'); fileService['s3'].uploadMedia = vi.fn().mockResolvedValue(undefined); // 执行 const result = await fileService.uploadMedia(testKey, testBuffer); // 验证 expect(fileService['s3'].uploadMedia).toHaveBeenCalledWith(testKey, testBuffer); expect(result).toEqual({ key: testKey }); }); it('当S3上传失败时应该抛出错误', async () => { // 准备 const testKey = 'images/test.jpg'; const testBuffer = Buffer.from('fake image data'); const uploadError = new Error('S3 upload failed'); fileService['s3'].uploadMedia = vi.fn().mockRejectedValue(uploadError); // 执行和验证 await expect(fileService.uploadMedia(testKey, testBuffer)).rejects.toThrow( 'S3 upload failed', ); expect(fileService['s3'].uploadMedia).toHaveBeenCalledWith(testKey, testBuffer); }); }); });