UNPKG

gs-download

Version:

Downloader of Genius Scan PDFs files

259 lines (210 loc) 8.22 kB
import { createWriteStream, existsSync, mkdirSync } from 'fs'; import { ClientRequest, get} from 'node:http'; import { vi } from 'vitest'; import ProgressManager from '../managers/progress-manager.js'; import FileInfo from '../models/file-info.model.js'; import { createMock, mockClientRequest, unsafeCast } from "../test/test-utils.js"; import logger from '../utils/logger.js'; import DownloadService from './download.service.js'; vi.mock('fs', () => ({ createWriteStream: vi.fn().mockReturnValue({ on: vi.fn().mockImplementation(function(this: any, event, callback) { if (event === 'finish') { callback(); } return this; }), close: vi.fn() }), existsSync: vi.fn().mockReturnValue(true), mkdirSync: vi.fn() })); vi.mock('node:http', () => ({ get: vi.fn() })); vi.mock('../utils/logger.js', () => ({ default: { info: vi.fn(), error: vi.fn() } })); const mockProgressManager = createMock<ProgressManager>({ create: vi.fn(), update: vi.fn(), done: vi.fn() }) describe('DownloadService', () => { let downloadService: DownloadService; let mockResponse: any; let mockFileInfo: FileInfo; beforeEach(() => { vi.clearAllMocks(); downloadService = new DownloadService(mockProgressManager); mockFileInfo = new FileInfo('test-uid', 'test-file.pdf'); mockResponse = { statusCode: 200, pipe: vi.fn(), headers: { 'content-length': '1000' }, on: vi.fn().mockImplementation((event, callback)=> { if (event === 'data') { callback(Buffer.from('test data')); } }) }; const mockClientRequest = createMock<ClientRequest>(); vi.mocked(unsafeCast<(url: string | URL, cb: any) => ClientRequest>(get)) .mockImplementation((url: string | URL, callback) => { callback(mockResponse); return mockClientRequest; }); global.fetch = vi.fn().mockResolvedValue({ ok: true, json: vi.fn().mockResolvedValue([ { uid: 'file1', name: 'File 1.pdf' }, { uid: 'file2', name: 'File 2.pdf' } ]) }); }); describe('downloadFile', () => { it('should download a file successfully', async () => { // given const baseUrl = 'http://example.com'; const outputDir = './downloads'; // when const result = await downloadService.downloadFile(mockFileInfo, baseUrl, outputDir); // then expect(result).toBe('downloads/test-file.pdf'); expect(get).toHaveBeenCalledWith('http://example.com/files/test-uid', expect.any(Function)); expect(createWriteStream).toHaveBeenCalledWith('downloads/test-file.pdf'); expect(mockProgressManager.create).toHaveBeenCalledWith(mockFileInfo.labeledName, 1000); expect(mockProgressManager.update).toHaveBeenCalledWith(mockFileInfo.labeledName, expect.any(Number)); expect(mockProgressManager.done).toHaveBeenCalledWith(mockFileInfo.labeledName); }); it('should throw an error if file UID is missing', async () => { // given const invalidFileInfo = new FileInfo('', 'test-file.pdf'); const baseUrl = 'http://example.com'; // when/then await expect(downloadService.downloadFile(invalidFileInfo, baseUrl)) .rejects.toThrow('File UID is required'); expect(logger.error).toHaveBeenCalledWith('Download failed: File UID is missing'); }); it('should throw an error if base URL is missing', async () => { // given const baseUrl = ''; // when/then await expect(downloadService.downloadFile(mockFileInfo, baseUrl)) .rejects.toThrow('Base URL is required'); expect(logger.error).toHaveBeenCalledWith('Download failed: Base URL is missing'); }); it('should create output directory if it does not exist', async () => { // given const baseUrl = 'http://example.com'; const outputDir = './downloads'; vi.mocked(existsSync).mockReturnValueOnce(false); // when await downloadService.downloadFile(mockFileInfo, baseUrl, outputDir); // then expect(existsSync).toHaveBeenCalledWith(outputDir); expect(mkdirSync).toHaveBeenCalledWith(outputDir, { recursive: true }); }); it('should handle HTTP error responses', async () => { // given const baseUrl = 'http://example.com'; mockResponse.statusCode = 404; // when/then await expect(downloadService.downloadFile(mockFileInfo, baseUrl)) .rejects.toThrow('Download failed: 404'); }); it('should handle network errors', async () => { // given const baseUrl = 'http://example.com'; vi.mocked(get).mockImplementation(() => { const clientRequestMock = mockClientRequest(); process.nextTick(() => { clientRequestMock.emit('error', new Error('Network error')); }) return clientRequestMock; }); // when/then await expect(downloadService.downloadFile(mockFileInfo, baseUrl)) .rejects.toThrow('Download failed: Network error'); expect(mockProgressManager.done).toHaveBeenCalledWith(mockFileInfo.labeledName); }); }); describe('retrieveFileList', () => { it('should retrieve file list successfully', async () => { // given const jsonFileUrl = 'http://example.com/files.json'; // when const result = await downloadService.retrieveFileList(jsonFileUrl); // then expect(result).toHaveLength(2); expect(result[0]).toBeInstanceOf(FileInfo); expect(result[0]?.uid).toBe('file1'); expect(result[0]?.name).toBe('File 1.pdf'); expect(result[1]).toBeInstanceOf(FileInfo); expect(result[1]?.uid).toBe('file2'); expect(result[1]?.name).toBe('File 2.pdf'); expect(fetch).toHaveBeenCalledWith(jsonFileUrl); }); it('should throw an error if JSON file URL is missing', async () => { // given const jsonFileUrl = ''; // when/then await expect(downloadService.retrieveFileList(jsonFileUrl)) .rejects.toThrow('JSON file URL is required'); expect(logger.error).toHaveBeenCalledWith('Download failed: JSON file URL is missing'); }); it('should throw an error if HTTP request fails', async () => { // given const jsonFileUrl = 'http://example.com/files.json'; global.fetch = vi.fn().mockResolvedValue({ ok: false, status: 404, statusText: 'Not Found' }); // when/then await expect(downloadService.retrieveFileList(jsonFileUrl)) .rejects.toThrow('HTTP error 404 Not Found'); expect(logger.error).toHaveBeenCalledWith('Download failed: HTTP error 404 Not Found'); }); it('should throw an error if response is not an array', async () => { // given const jsonFileUrl = 'http://example.com/files.json'; global.fetch = vi.fn().mockResolvedValue({ ok: true, json: vi.fn().mockResolvedValue({ notAnArray: true }) }); // when/then await expect(downloadService.retrieveFileList(jsonFileUrl)) .rejects.toThrow('Invalid JSON format: expected an array'); }); it('should throw an error if array items are missing required properties', async () => { // given const jsonFileUrl = 'http://example.com/files.json'; global.fetch = vi.fn().mockResolvedValue({ ok: true, json: vi.fn().mockResolvedValue([ { uid: 'file1' }, { name: 'File 2.pdf' } ]) }); // when/then await expect(downloadService.retrieveFileList(jsonFileUrl)) .rejects.toThrow('Invalid JSON format: items must have uid and name'); }); it('should handle fetch errors', async () => { // given const jsonFileUrl = 'http://example.com/files.json'; const fetchError = new Error('Network error'); global.fetch = vi.fn().mockRejectedValue(fetchError); // when/then await expect(downloadService.retrieveFileList(jsonFileUrl)) .rejects.toThrow('Failed to retrieve file list: Network error'); expect(logger.error).toHaveBeenCalledWith('Failed to retrieve file list: Network error'); }); }); });