diff --git a/tests/upload-manager.test.js b/tests/upload-manager.test.js index be64771..3b2717f 100644 --- a/tests/upload-manager.test.js +++ b/tests/upload-manager.test.js @@ -280,6 +280,86 @@ describe('UploadManager', () => { await assert.rejects(mgr._sleep(5000, ac.signal), /Aborted/); }); + it('file not found produces descriptive error', async () => { + // Override fs.statSync to throw ENOENT for a specific path + const fs = require('fs'); + const origStat = fs.statSync; + fs.statSync = function(p) { + if (p === '/test/deleted.mp4') throw Object.assign(new Error('ENOENT'), { code: 'ENOENT' }); + return origStat.call(this, p); + }; + + const mgr = new UploadManager({}); + const errors = []; + mgr.on('progress', (d) => { if (d.error) errors.push(d.error); }); + + await mgr.startBatch([ + { file: '/test/deleted.mp4', hoster: 'doodstream.com', apiKey: 'key1' } + ]); + + fs.statSync = origStat; + assert.ok(errors.some(e => e.includes('nicht gefunden')), `expected "nicht gefunden" error, got: ${errors.join(', ')}`); + }); + + it('zero-byte file produces descriptive error', async () => { + fakeFileSize = 0; + + const mgr = new UploadManager({}); + const errors = []; + mgr.on('progress', (d) => { if (d.error) errors.push(d.error); }); + + await mgr.startBatch([ + { file: '/test/empty.mp4', hoster: 'doodstream.com', apiKey: 'key1' } + ]); + + assert.ok(errors.some(e => e.includes('0 Bytes')), `expected "0 Bytes" error, got: ${errors.join(', ')}`); + }); + + it('empty batch completes immediately with zero counts', async () => { + const mgr = new UploadManager({}); + let summary = null; + mgr.on('batch-done', (s) => { summary = s; }); + + const start = Date.now(); + await mgr.startBatch([]); + const elapsed = Date.now() - start; + + assert.ok(summary, 'batch-done should be emitted'); + assert.equal(summary.total, 0); + assert.equal(summary.succeeded, 0); + assert.equal(summary.failed, 0); + assert.ok(elapsed < 200, `empty batch should complete fast, took ${elapsed}ms`); + }); + + it('scaleParallelUploads limits per-hoster count to global limit', async () => { + let concurrent = 0; + let maxConcurrent = 0; + + mockUploadFile.mock.mockImplementation(async (hoster, filePath, apiKey, onProgress) => { + concurrent++; + maxConcurrent = Math.max(maxConcurrent, concurrent); + await new Promise(r => setTimeout(r, 40)); + concurrent--; + if (onProgress) onProgress(fakeFileSize, fakeFileSize); + return { download_url: 'ok', embed_url: null, file_code: 'ok' }; + }); + + const mgr = new UploadManager( + { 'doodstream.com': { retries: 0, parallelCount: 10, maxSpeedKbs: 0, restartBelowKbs: 0, timeIntervalSec: 0, maxSizeMb: 0 } }, + { parallelUploadCount: 2, scaleParallelUploads: true } + ); + + await mgr.startBatch([ + { file: '/test/a.mp4', hoster: 'doodstream.com', apiKey: 'k' }, + { file: '/test/b.mp4', hoster: 'doodstream.com', apiKey: 'k' }, + { file: '/test/c.mp4', hoster: 'doodstream.com', apiKey: 'k' }, + { file: '/test/d.mp4', hoster: 'doodstream.com', apiKey: 'k' }, + { file: '/test/e.mp4', hoster: 'doodstream.com', apiKey: 'k' } + ]); + + assert.ok(maxConcurrent <= 2, `scaleParallelUploads should cap at 2, was ${maxConcurrent}`); + }); + it('stats event contains expected fields', async () => { // Make upload take long enough for stats interval to fire mockUploadFile.mock.mockImplementation(async (hoster, filePath, apiKey, onProgress, signal) => {