Files
simstudioai--sim/apps/sim/lib/file-parsers/index.test.ts
T
wehub-resource-sync d25d482dc2
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:20:55 +08:00

338 lines
10 KiB
TypeScript

/**
* @vitest-environment node
*/
import path from 'path'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import type { FileParseResult, FileParser } from '@/lib/file-parsers/types'
const {
mockExistsSync,
mockReadFile,
mockPdfParseFile,
mockCsvParseFile,
mockDocxParseFile,
mockTxtParseFile,
mockMdParseFile,
mockPptxParseFile,
mockHtmlParseFile,
} = vi.hoisted(() => ({
mockExistsSync: vi.fn().mockReturnValue(true),
mockReadFile: vi.fn().mockResolvedValue(Buffer.from('test content')),
mockPdfParseFile: vi.fn().mockResolvedValue({
content: 'Parsed PDF content',
metadata: { info: { Title: 'Test PDF' }, pageCount: 5, version: '1.7' },
}),
mockCsvParseFile: vi.fn().mockResolvedValue({
content: 'Parsed CSV content',
metadata: { headers: ['column1', 'column2'], rowCount: 10 },
}),
mockDocxParseFile: vi.fn().mockResolvedValue({
content: 'Parsed DOCX content',
metadata: { pages: 3, author: 'Test Author' },
}),
mockTxtParseFile: vi.fn().mockResolvedValue({
content: 'Parsed TXT content',
metadata: { characterCount: 100, tokenCount: 10 },
}),
mockMdParseFile: vi.fn().mockResolvedValue({
content: 'Parsed MD content',
metadata: { characterCount: 100, tokenCount: 10 },
}),
mockPptxParseFile: vi.fn().mockResolvedValue({
content: 'Parsed PPTX content',
metadata: { slideCount: 5, extractionMethod: 'officeparser' },
}),
mockHtmlParseFile: vi.fn().mockResolvedValue({
content: 'Parsed HTML content',
metadata: { title: 'Test HTML Document', headingCount: 3, linkCount: 2 },
}),
}))
vi.mock('fs', () => ({ existsSync: mockExistsSync }))
vi.mock('fs/promises', () => ({ readFile: mockReadFile }))
vi.mock('@/lib/file-parsers/index', () => {
const mockParsers: Record<string, FileParser> = {
pdf: { parseFile: mockPdfParseFile },
csv: { parseFile: mockCsvParseFile },
docx: { parseFile: mockDocxParseFile },
txt: { parseFile: mockTxtParseFile },
md: { parseFile: mockMdParseFile },
pptx: { parseFile: mockPptxParseFile },
ppt: { parseFile: mockPptxParseFile },
html: { parseFile: mockHtmlParseFile },
htm: { parseFile: mockHtmlParseFile },
}
return {
parseFile: async (filePath: string): Promise<FileParseResult> => {
if (!filePath) {
throw new Error('No file path provided')
}
if (!mockExistsSync(filePath)) {
throw new Error(`File not found: ${filePath}`)
}
const extension = path.extname(filePath).toLowerCase().substring(1)
if (!Object.keys(mockParsers).includes(extension)) {
throw new Error(
`Unsupported file type: ${extension}. Supported types are: ${Object.keys(mockParsers).join(', ')}`
)
}
return mockParsers[extension].parseFile(filePath)
},
isSupportedFileType: (extension: string): boolean => {
if (!extension) return false
return Object.keys(mockParsers).includes(extension.toLowerCase())
},
}
})
vi.mock('@/lib/file-parsers/pdf-parser', () => ({
PdfParser: vi.fn().mockImplementation(() => ({ parseFile: mockPdfParseFile })),
}))
vi.mock('@/lib/file-parsers/csv-parser', () => ({
CsvParser: vi.fn().mockImplementation(() => ({ parseFile: mockCsvParseFile })),
}))
vi.mock('@/lib/file-parsers/docx-parser', () => ({
DocxParser: vi.fn().mockImplementation(() => ({ parseFile: mockDocxParseFile })),
}))
vi.mock('@/lib/file-parsers/txt-parser', () => ({
TxtParser: vi.fn().mockImplementation(() => ({ parseFile: mockTxtParseFile })),
}))
vi.mock('@/lib/file-parsers/md-parser', () => ({
MdParser: vi.fn().mockImplementation(() => ({ parseFile: mockMdParseFile })),
}))
vi.mock('@/lib/file-parsers/pptx-parser', () => ({
PptxParser: vi.fn().mockImplementation(() => ({ parseFile: mockPptxParseFile })),
}))
vi.mock('@/lib/file-parsers/html-parser', () => ({
HtmlParser: vi.fn().mockImplementation(() => ({ parseFile: mockHtmlParseFile })),
}))
import { isSupportedFileType, parseFile } from '@/lib/file-parsers/index'
describe('File Parsers', () => {
beforeEach(() => {
vi.clearAllMocks()
mockExistsSync.mockReturnValue(true)
})
describe('parseFile', () => {
it('should validate file existence', async () => {
mockExistsSync.mockReturnValueOnce(false)
const testFilePath = '/test/files/test.pdf'
await expect(parseFile(testFilePath)).rejects.toThrow('File not found')
expect(mockExistsSync).toHaveBeenCalledWith(testFilePath)
})
it('should throw error if file path is empty', async () => {
await expect(parseFile('')).rejects.toThrow('No file path provided')
})
it('should parse PDF files successfully', async () => {
const expectedResult = {
content: 'Parsed PDF content',
metadata: {
info: { Title: 'Test PDF' },
pageCount: 5,
version: '1.7',
},
}
mockPdfParseFile.mockResolvedValueOnce(expectedResult)
const result = await parseFile('/test/files/document.pdf')
expect(result).toEqual(expectedResult)
})
it('should parse CSV files successfully', async () => {
const expectedResult = {
content: 'Parsed CSV content',
metadata: {
headers: ['column1', 'column2'],
rowCount: 10,
},
}
mockCsvParseFile.mockResolvedValueOnce(expectedResult)
const result = await parseFile('/test/files/data.csv')
expect(result).toEqual(expectedResult)
})
it('should parse DOCX files successfully', async () => {
const expectedResult = {
content: 'Parsed DOCX content',
metadata: {
pages: 3,
author: 'Test Author',
},
}
mockDocxParseFile.mockResolvedValueOnce(expectedResult)
const result = await parseFile('/test/files/document.docx')
expect(result).toEqual(expectedResult)
})
it('should parse TXT files successfully', async () => {
const expectedResult = {
content: 'Parsed TXT content',
metadata: {
characterCount: 100,
tokenCount: 10,
},
}
mockTxtParseFile.mockResolvedValueOnce(expectedResult)
const result = await parseFile('/test/files/document.txt')
expect(result).toEqual(expectedResult)
})
it('should parse MD files successfully', async () => {
const expectedResult = {
content: 'Parsed MD content',
metadata: {
characterCount: 100,
tokenCount: 10,
},
}
mockMdParseFile.mockResolvedValueOnce(expectedResult)
const result = await parseFile('/test/files/document.md')
expect(result).toEqual(expectedResult)
})
it('should parse PPTX files successfully', async () => {
const expectedResult = {
content: 'Parsed PPTX content',
metadata: {
slideCount: 5,
extractionMethod: 'officeparser',
},
}
mockPptxParseFile.mockResolvedValueOnce(expectedResult)
const result = await parseFile('/test/files/presentation.pptx')
expect(result).toEqual(expectedResult)
})
it('should parse PPT files successfully', async () => {
const expectedResult = {
content: 'Parsed PPTX content',
metadata: {
slideCount: 5,
extractionMethod: 'officeparser',
},
}
mockPptxParseFile.mockResolvedValueOnce(expectedResult)
const result = await parseFile('/test/files/presentation.ppt')
expect(result).toEqual(expectedResult)
})
it('should parse HTML files successfully', async () => {
const expectedResult = {
content: 'Parsed HTML content',
metadata: {
title: 'Test HTML Document',
headingCount: 3,
linkCount: 2,
},
}
mockHtmlParseFile.mockResolvedValueOnce(expectedResult)
const result = await parseFile('/test/files/document.html')
expect(result).toEqual(expectedResult)
})
it('should parse HTM files successfully', async () => {
const expectedResult = {
content: 'Parsed HTML content',
metadata: {
title: 'Test HTML Document',
headingCount: 3,
linkCount: 2,
},
}
mockHtmlParseFile.mockResolvedValueOnce(expectedResult)
const result = await parseFile('/test/files/document.htm')
expect(result).toEqual(expectedResult)
})
it('should throw error for unsupported file types', async () => {
const unsupportedFilePath = '/test/files/image.png'
await expect(parseFile(unsupportedFilePath)).rejects.toThrow('Unsupported file type')
})
it('should handle errors during parsing', async () => {
const parsingError = new Error('CSV parsing failed')
mockCsvParseFile.mockRejectedValueOnce(parsingError)
await expect(parseFile('/test/files/data.csv')).rejects.toThrow('CSV parsing failed')
})
})
describe('isSupportedFileType', () => {
it('should return true for supported file types', () => {
expect(isSupportedFileType('pdf')).toBe(true)
expect(isSupportedFileType('csv')).toBe(true)
expect(isSupportedFileType('docx')).toBe(true)
expect(isSupportedFileType('txt')).toBe(true)
expect(isSupportedFileType('md')).toBe(true)
expect(isSupportedFileType('pptx')).toBe(true)
expect(isSupportedFileType('ppt')).toBe(true)
expect(isSupportedFileType('html')).toBe(true)
expect(isSupportedFileType('htm')).toBe(true)
})
it('should return false for unsupported file types', () => {
expect(isSupportedFileType('png')).toBe(false)
expect(isSupportedFileType('unknown')).toBe(false)
})
it('should handle uppercase extensions', () => {
expect(isSupportedFileType('PDF')).toBe(true)
expect(isSupportedFileType('CSV')).toBe(true)
expect(isSupportedFileType('TXT')).toBe(true)
expect(isSupportedFileType('MD')).toBe(true)
expect(isSupportedFileType('PPTX')).toBe(true)
expect(isSupportedFileType('HTML')).toBe(true)
})
it('should handle errors gracefully', () => {
/**
* This test verifies error propagation. The mock factory for
* isSupportedFileType already handles this via the parseFile mock
* which throws for unsupported types. We test by verifying the
* function doesn't silently swallow errors from the parser lookup.
*/
expect(() => isSupportedFileType('')).not.toThrow()
expect(isSupportedFileType('')).toBe(false)
})
})
})