Reproduced from a real saved config: pendingQueue held 4 'preview' jobs (one file across 4 hosters); the queue saved + restored correctly. But _autoDeduplicateFromLog (runs at init after restore) removed jobs whose fileName|hoster appeared ANYWHERE in the lifetime fileuploader.log, regardless of status — so all 4 pending previews were deleted and the queue showed the empty "Dateien hierhin ziehen" state. Looked update-specific only because the server restarts on update; a plain restart did the same. - New lib/queue-dedup.js (pure, dual CJS/window export like queue-prune.js): partitionRestoredJobsByLog drops ONLY 'done' jobs that match the log. Pending (preview/queued) and failed (error/aborted) jobs always survive — they're intentional queued work (often a deliberate re-upload of a previously uploaded file). Manual importUploadLog stays separate/explicit. - renderer wires it in; index.html loads the module before app.js. - Tests: 5 cases incl. the exact reproduced scenario (4 previews all in log -> 0 removed). Full suite 162/162. Verified against the user's real electron-config.json + fileuploader.log: old logic removed 4/4 (empty queue), new logic removes 0/4 (queue preserved). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
73 lines
3.2 KiB
JavaScript
73 lines
3.2 KiB
JavaScript
const { test } = require('node:test');
|
|
const assert = require('node:assert');
|
|
const { partitionRestoredJobsByLog } = require('../lib/queue-dedup');
|
|
|
|
function job(status, fileName, hoster) {
|
|
return { status, fileName, hoster, file: `C:/dl/${fileName}` };
|
|
}
|
|
|
|
test('regression: pending preview jobs are NEVER dropped, even when all match the log', () => {
|
|
// Exact shape of the reproduced bug: 4 preview jobs for one file across 4
|
|
// hosters, every fileName|hoster present in the lifetime upload log.
|
|
const jobs = [
|
|
job('preview', 'Einfach mal die Fresse halten!!!.mp4', 'doodstream.com'),
|
|
job('preview', 'Einfach mal die Fresse halten!!!.mp4', 'voe.sx'),
|
|
job('preview', 'Einfach mal die Fresse halten!!!.mp4', 'vidmoly.me'),
|
|
job('preview', 'Einfach mal die Fresse halten!!!.mp4', 'byse.sx')
|
|
];
|
|
const log = [
|
|
{ fileName: 'Einfach mal die Fresse halten!!!.mp4', hoster: 'doodstream.com' },
|
|
{ fileName: 'Einfach mal die Fresse halten!!!.mp4', hoster: 'voe.sx' },
|
|
{ fileName: 'Einfach mal die Fresse halten!!!.mp4', hoster: 'vidmoly.me' },
|
|
{ fileName: 'Einfach mal die Fresse halten!!!.mp4', hoster: 'byse.sx' }
|
|
];
|
|
const { kept, removed } = partitionRestoredJobsByLog(jobs, log);
|
|
assert.equal(removed.length, 0, 'no pending job may be removed');
|
|
assert.equal(kept.length, 4, 'all 4 pending jobs survive restart/update');
|
|
});
|
|
|
|
test('done jobs in the log are dropped (declutter); pending/error/aborted kept', () => {
|
|
const jobs = [
|
|
job('done', 'a.mkv', 'doodstream.com'),
|
|
job('preview', 'a.mkv', 'voe.sx'),
|
|
job('error', 'b.mkv', 'doodstream.com'),
|
|
job('aborted', 'c.mkv', 'doodstream.com')
|
|
];
|
|
const log = [
|
|
{ fileName: 'a.mkv', hoster: 'doodstream.com' },
|
|
{ fileName: 'a.mkv', hoster: 'voe.sx' },
|
|
{ fileName: 'b.mkv', hoster: 'doodstream.com' },
|
|
{ fileName: 'c.mkv', hoster: 'doodstream.com' }
|
|
];
|
|
const { kept, removed } = partitionRestoredJobsByLog(jobs, log);
|
|
assert.equal(removed.length, 1);
|
|
assert.equal(removed[0].status, 'done');
|
|
assert.equal(removed[0].hoster, 'doodstream.com');
|
|
// The preview a.mkv|voe.sx, error b.mkv, aborted c.mkv all survive.
|
|
assert.equal(kept.length, 3);
|
|
assert.ok(kept.some(j => j.status === 'preview' && j.hoster === 'voe.sx'));
|
|
assert.ok(kept.some(j => j.status === 'error'));
|
|
assert.ok(kept.some(j => j.status === 'aborted'));
|
|
});
|
|
|
|
test('done job NOT in the log is kept (e.g. hoster had logToFile disabled)', () => {
|
|
const jobs = [job('done', 'd.mkv', 'doodstream.com')];
|
|
const { kept, removed } = partitionRestoredJobsByLog(jobs, []);
|
|
assert.equal(removed.length, 0);
|
|
assert.equal(kept.length, 1);
|
|
});
|
|
|
|
test('case-insensitive match on fileName and hoster', () => {
|
|
const jobs = [job('done', 'Movie.MKV', 'DoodStream.com')];
|
|
const log = [{ fileName: 'movie.mkv', hoster: 'doodstream.com' }];
|
|
const { removed } = partitionRestoredJobsByLog(jobs, log);
|
|
assert.equal(removed.length, 1);
|
|
});
|
|
|
|
test('empty/missing inputs do not throw', () => {
|
|
assert.deepEqual(partitionRestoredJobsByLog([], []), { kept: [], removed: [] });
|
|
assert.deepEqual(partitionRestoredJobsByLog(null, null), { kept: [], removed: [] });
|
|
const jobs = [job('done', 'x.mkv', 'voe.sx')];
|
|
assert.equal(partitionRestoredJobsByLog(jobs, undefined).kept.length, 1);
|
|
});
|