test(web): cover VisionFile normalization branches

This commit is contained in:
Haohao-end 2026-03-14 14:31:46 +08:00
parent c7256e6891
commit 5be09d8df1
2 changed files with 79 additions and 18 deletions

View File

@ -4,7 +4,13 @@ import type { VisionFile } from '@/types/app'
import type { FileResponse } from '@/types/workflow'
import { describe, expect, it } from 'vitest'
import { TransferMethod } from '@/types/app'
import { addFileInfos, sortAgentSorts } from '../index'
import {
addFileInfos,
getVisionFileMimeType,
getVisionFileName,
getVisionFileSupportType,
sortAgentSorts,
} from '../index'
describe('tools/utils', () => {
const createFileEntity = (overrides: Partial<FileEntity>): FileEntity => ({
@ -54,6 +60,46 @@ describe('tools/utils', () => {
})
})
describe('VisionFile helpers', () => {
it.each([
['image', 'image/png'],
['video', 'video/mp4'],
['audio', 'audio/mpeg'],
['application/pdf', 'application/pdf'],
['document', 'application/octet-stream'],
['unknown', 'application/octet-stream'],
])('returns %s mime type as %s', (fileType, expectedMimeType) => {
expect(getVisionFileMimeType(fileType)).toBe(expectedMimeType)
})
it.each([
['image', 'image'],
['video', 'video'],
['audio', 'audio'],
['document', 'document'],
['image/png', 'image'],
['video/mp4', 'video'],
['audio/mpeg', 'audio'],
['application/pdf', 'document'],
['application/json', 'document'],
] as const)('returns %s support type as %s', (fileType, expectedSupportType) => {
expect(getVisionFileSupportType(fileType)).toBe(expectedSupportType)
})
it('extracts the file name from URLs with query params', () => {
expect(getVisionFileName('https://example.com/generated.png?signature=1', 'image')).toBe('generated.png')
})
it.each([
['image', 'generated_image.png'],
['video', 'generated_video.mp4'],
['audio', 'generated_audio.mp3'],
['document', 'generated_file.bin'],
] as const)('returns a fallback file name for %s URLs without a file segment', (supportFileType, expectedFileName) => {
expect(getVisionFileName('https://example.com/', supportFileType)).toBe(expectedFileName)
})
})
describe('addFileInfos', () => {
it('returns null/undefined input as-is', () => {
expect(addFileInfos(null as unknown as ThoughtItem[], [])).toBeNull()
@ -131,6 +177,30 @@ describe('tools/utils', () => {
}))
})
it('matches VisionFile payloads by upload_file_id when id is missing', () => {
const visionFile: VisionFile = {
type: 'document',
transfer_method: TransferMethod.remote_url,
url: 'https://example.com/',
upload_file_id: 'upload-vision-fallback',
}
const items = [{ id: '1', files: ['upload-vision-fallback'] }] as unknown as ThoughtItem[]
const result = addFileInfos(items, [visionFile])
const messageFiles = (result[0] as ThoughtItem & { message_files: FileEntity[] }).message_files
expect(messageFiles).toHaveLength(1)
expect(messageFiles[0]).toEqual(expect.objectContaining({
id: 'upload-vision-fallback',
name: 'generated_file.bin',
type: 'application/octet-stream',
transferMethod: TransferMethod.remote_url,
supportFileType: 'document',
uploadedId: 'upload-vision-fallback',
}))
})
it('returns items without files unchanged', () => {
const items = [
{ id: '1' },

View File

@ -32,6 +32,7 @@ export const sortAgentSorts = (list: ThoughtItem[]) => {
type AgentThoughtHistoryFile = FileResponse & { id: string }
type AgentThoughtMessageFile = FileEntity | VisionFile | AgentThoughtHistoryFile
type VisionFileSupportType = 'image' | 'video' | 'audio' | 'document'
const isFileEntity = (file: AgentThoughtMessageFile): file is FileEntity => {
return 'transferMethod' in file
@ -41,7 +42,7 @@ const isAgentThoughtHistoryFile = (file: AgentThoughtMessageFile): file is Agent
return 'filename' in file && 'mime_type' in file
}
const getVisionFileMimeType = (fileType: string) => {
export const getVisionFileMimeType = (fileType: string) => {
if (fileType.includes('/'))
return fileType
@ -57,35 +58,25 @@ const getVisionFileMimeType = (fileType: string) => {
}
}
const getVisionFileSupportType = (fileType: string) => {
export const getVisionFileSupportType = (fileType: string): VisionFileSupportType => {
if (fileType.includes('/')) {
const [mainType, subType] = fileType.split('/')
if (mainType === 'image')
return 'image'
if (mainType === 'video')
return 'video'
if (mainType === 'audio')
return 'audio'
if (subType === 'pdf')
return 'document'
const [mainType] = fileType.split('/')
if (['image', 'video', 'audio'].includes(mainType))
return mainType as 'image' | 'video' | 'audio'
return 'document'
}
switch (fileType) {
case 'image':
return 'image'
case 'video':
return 'video'
case 'audio':
return 'audio'
case 'document':
return 'document'
return fileType
default:
return 'document'
}
}
const getVisionFileName = (url: string, supportFileType: string) => {
export const getVisionFileName = (url: string, supportFileType: VisionFileSupportType) => {
const fileName = url.split('/').pop()?.split('?')[0]
if (fileName)
return fileName