fix: #3727 LLM model download fail can still be used (#3731)

* fix: #3727 - LLM model download fail can still be used

* test: add tests

* test: fix path on Windows
This commit is contained in:
Louis 2024-09-25 09:46:46 +07:00 committed by GitHub
parent dbc4bed40f
commit f46ab45e0e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 120 additions and 44 deletions

View File

@ -1,59 +1,131 @@
import { Downloader } from './download'; import { Downloader } from './download'
import { DownloadEvent } from '../../../types/api'; import { DownloadEvent } from '../../../types/api'
import { DownloadManager } from '../../helper/download'; import { DownloadManager } from '../../helper/download'
it('should handle getFileSize errors correctly', async () => { jest.mock('../../helper', () => ({
const observer = jest.fn(); getJanDataFolderPath: jest.fn().mockReturnValue('path/to/folder'),
const url = 'http://example.com/file'; }))
const downloader = new Downloader(observer); jest.mock('../../helper/path', () => ({
const requestMock = jest.fn((options, callback) => { validatePath: jest.fn().mockReturnValue('path/to/folder'),
callback(new Error('Test error'), null); normalizeFilePath: () => process.platform === 'win32' ? 'C:\\Users\path\\to\\file.gguf' : '/Users/path/to/file.gguf',
}); }))
jest.mock('request', () => requestMock);
await expect(downloader.getFileSize(observer, url)).rejects.toThrow('Test error'); jest.mock(
}); 'request',
jest.fn().mockReturnValue(() => ({
on: jest.fn(),
}))
)
jest.mock('fs', () => ({
createWriteStream: jest.fn(),
}))
it('should pause download correctly', () => { jest.mock('request-progress', () => {
const observer = jest.fn(); return jest.fn().mockImplementation(() => {
const fileName = process.platform === 'win32' ? 'C:\\path\\to\\file' : 'path/to/file'; return {
on: jest.fn().mockImplementation((event, callback) => {
if (event === 'error') {
callback(new Error('Download failed'))
}
return {
on: jest.fn().mockImplementation((event, callback) => {
if (event === 'error') {
callback(new Error('Download failed'))
}
return {
on: jest.fn().mockImplementation((event, callback) => {
if (event === 'error') {
callback(new Error('Download failed'))
}
return { pipe: jest.fn() }
}),
}
}),
}
}),
}
})
})
const downloader = new Downloader(observer); describe('Downloader', () => {
const pauseMock = jest.fn(); beforeEach(() => {
DownloadManager.instance.networkRequests[fileName] = { pause: pauseMock }; jest.resetAllMocks()
})
it('should handle getFileSize errors correctly', async () => {
const observer = jest.fn()
const url = 'http://example.com/file'
downloader.pauseDownload(observer, fileName); const downloader = new Downloader(observer)
const requestMock = jest.fn((options, callback) => {
callback(new Error('Test error'), null)
})
jest.mock('request', () => requestMock)
expect(pauseMock).toHaveBeenCalled(); await expect(downloader.getFileSize(observer, url)).rejects.toThrow('Test error')
}); })
it('should resume download correctly', () => { it('should pause download correctly', () => {
const observer = jest.fn(); const observer = jest.fn()
const fileName = process.platform === 'win32' ? 'C:\\path\\to\\file' : 'path/to/file'; const fileName = process.platform === 'win32' ? 'C:\\path\\to\\file' : 'path/to/file'
const downloader = new Downloader(observer); const downloader = new Downloader(observer)
const resumeMock = jest.fn(); const pauseMock = jest.fn()
DownloadManager.instance.networkRequests[fileName] = { resume: resumeMock }; DownloadManager.instance.networkRequests[fileName] = { pause: pauseMock }
downloader.resumeDownload(observer, fileName); downloader.pauseDownload(observer, fileName)
expect(resumeMock).toHaveBeenCalled(); expect(pauseMock).toHaveBeenCalled()
}); })
it('should handle aborting a download correctly', () => { it('should resume download correctly', () => {
const observer = jest.fn(); const observer = jest.fn()
const fileName = process.platform === 'win32' ? 'C:\\path\\to\\file' : 'path/to/file'; const fileName = process.platform === 'win32' ? 'C:\\path\\to\\file' : 'path/to/file'
const downloader = new Downloader(observer); const downloader = new Downloader(observer)
const abortMock = jest.fn(); const resumeMock = jest.fn()
DownloadManager.instance.networkRequests[fileName] = { abort: abortMock }; DownloadManager.instance.networkRequests[fileName] = { resume: resumeMock }
downloader.abortDownload(observer, fileName); downloader.resumeDownload(observer, fileName)
expect(abortMock).toHaveBeenCalled(); expect(resumeMock).toHaveBeenCalled()
expect(observer).toHaveBeenCalledWith(DownloadEvent.onFileDownloadError, expect.objectContaining({ })
error: 'aborted'
})); it('should handle aborting a download correctly', () => {
}); const observer = jest.fn()
const fileName = process.platform === 'win32' ? 'C:\\path\\to\\file' : 'path/to/file'
const downloader = new Downloader(observer)
const abortMock = jest.fn()
DownloadManager.instance.networkRequests[fileName] = { abort: abortMock }
downloader.abortDownload(observer, fileName)
expect(abortMock).toHaveBeenCalled()
expect(observer).toHaveBeenCalledWith(
DownloadEvent.onFileDownloadError,
expect.objectContaining({
error: 'aborted',
})
)
})
it('should handle download fail correctly', () => {
const observer = jest.fn()
const fileName = process.platform === 'win32' ? 'C:\\path\\to\\file' : 'path/to/file.gguf'
const downloader = new Downloader(observer)
downloader.downloadFile(observer, {
localPath: fileName,
url: 'http://127.0.0.1',
})
expect(observer).toHaveBeenCalledWith(
DownloadEvent.onFileDownloadError,
expect.objectContaining({
error: expect.anything(),
})
)
})
})

View File

@ -100,7 +100,11 @@ export class Downloader implements Processor {
}) })
.on('end', () => { .on('end', () => {
const currentDownloadState = DownloadManager.instance.downloadProgressMap[modelId] const currentDownloadState = DownloadManager.instance.downloadProgressMap[modelId]
if (currentDownloadState && DownloadManager.instance.networkRequests[normalizedPath]) { if (
currentDownloadState &&
DownloadManager.instance.networkRequests[normalizedPath] &&
DownloadManager.instance.downloadProgressMap[modelId]?.downloadState !== 'error'
) {
// Finished downloading, rename temp file to actual file // Finished downloading, rename temp file to actual file
renameSync(downloadingTempFile, destination) renameSync(downloadingTempFile, destination)
const downloadState: DownloadState = { const downloadState: DownloadState = {