// S3 前端逻辑(docs/30):本地图片识别、配置完整性、相对路径拼绝对路径、对象文件名。 import { describe, it, expect } from 'vitest' import { isLocalImageSrc, isS3Configured, resolveDocRelativePath, imageFileName, isUploadableImageSrc, parseDataImage, dataImageFileName, DEFAULT_S3_SETTINGS } from '../src/services/s3' describe('isLocalImageSrc', () => { it('相对路径算本地;http/data/blob 不算', () => { expect(isLocalImageSrc('doc.assets/a.png')).toBe(true) expect(isLocalImageSrc('../shared/a.png')).toBe(true) expect(isLocalImageSrc('https://cdn.example.com/a.png')).toBe(false) expect(isLocalImageSrc('http://x/a.png')).toBe(false) expect(isLocalImageSrc('data:image/png;base64,xx')).toBe(false) expect(isLocalImageSrc('blob:abc')).toBe(false) expect(isLocalImageSrc('')).toBe(false) }) }) describe('base64 内嵌图片', () => { const png = 'data:image/png;base64,UE5HREFUQS0xMjM=' it('data:image 算未上传;普通 data: 与网络图不算', () => { expect(isUploadableImageSrc(png)).toBe(true) expect(isUploadableImageSrc('data:text/plain;base64,xx')).toBe(false) expect(isUploadableImageSrc('https://x/a.png')).toBe(false) }) it('解析出 mime 与纯 base64(剥空白)', () => { expect(parseDataImage(png)).toEqual({ mime: 'image/png', base64: 'UE5HREFUQS0xMjM=' }) expect(parseDataImage('data:image/jpeg;base64,AA\nBB')).toEqual({ mime: 'image/jpeg', base64: 'AABB' }) expect(parseDataImage('doc.assets/a.png')).toBeNull() }) it('对象名按内容哈希:同图同名、异图异名,jpeg 归一为 jpg', () => { const a = dataImageFileName(png) expect(a).toMatch(/^image-[0-9a-f]{8}\.png$/) expect(dataImageFileName(png)).toBe(a) expect(dataImageFileName('data:image/png;base64,QUJD')).not.toBe(a) expect(dataImageFileName('data:image/jpeg;base64,QUJD')?.endsWith('.jpg')).toBe(true) expect(dataImageFileName('data:image/svg+xml;base64,QUJD')?.endsWith('.svg')).toBe(true) expect(dataImageFileName('doc.assets/a.png')).toBeNull() }) }) describe('isS3Configured', () => { it('四项必填齐才算配置过', () => { expect(isS3Configured(DEFAULT_S3_SETTINGS)).toBe(false) expect(isS3Configured({ ...DEFAULT_S3_SETTINGS, bucket: 'b', region: 'r', accessKey: 'a', secretKey: 's' })).toBe(true) expect(isS3Configured({ ...DEFAULT_S3_SETTINGS, bucket: 'b', region: 'r', accessKey: 'a' })).toBe(false) }) }) describe('resolveDocRelativePath', () => { it('文档同目录 .assets 拼接(含 Windows 路径与 ..)', () => { expect(resolveDocRelativePath('D:/notes/todo.md', 'todo.assets/a.png')).toBe('D:/notes/todo.assets/a.png') expect(resolveDocRelativePath('D:\\notes\\todo.md', 'todo.assets\\a.png')).toBe('D:/notes/todo.assets/a.png') expect(resolveDocRelativePath('/home/u/notes/todo.md', '../shared/a.png')).toBe('/home/u/shared/a.png') expect(resolveDocRelativePath('/home/u/notes/todo.md', 'a%20b.png')).toBe('/home/u/notes/a b.png') expect(resolveDocRelativePath('/home/u/notes/todo.md', 'C:/abs/a.png')).toBe('C:/abs/a.png') }) }) describe('imageFileName', () => { it('取文件名(含解码与反斜杠)', () => { expect(imageFileName('doc.assets/图 1.png')).toBe('图 1.png') expect(imageFileName('a%20b.png')).toBe('a b.png') expect(imageFileName('x\\y\\z.jpg')).toBe('z.jpg') }) })