Persist job-ID terminal outcomes before final queue acknowledgement and clear recovery evidence only after both the terminal marker and renderer queue are durable.
95 lines
4.5 KiB
JavaScript
95 lines
4.5 KiB
JavaScript
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
|
|
test('catastrophic batch starts retain exact terminal outcomes for every job', () => {
|
|
const { buildFailedUploadSummary, buildTerminalJobSnapshots } = require('../lib/upload-recovery');
|
|
const summary = buildFailedUploadSummary([
|
|
{ jobId: 'job-a', file: 'C:\\one\\same.mkv', hoster: 'doodstream.com' },
|
|
{ jobId: 'job-b', file: 'D:\\two\\same.mkv', hoster: 'voe.sx' }
|
|
], 'Upload konnte nicht gestartet werden', Date.UTC(2026, 7, 13));
|
|
assert.equal(summary.total, 2);
|
|
assert.equal(summary.failed, 2);
|
|
assert.deepEqual(buildTerminalJobSnapshots(summary).map(entry => [entry.jobId, entry.status]), [
|
|
['job-a', 'error'],
|
|
['job-b', 'error']
|
|
]);
|
|
});
|
|
|
|
test('terminal recovery snapshots retain exact job outcomes and canonical links', () => {
|
|
const { buildTerminalJobSnapshots } = require('../lib/upload-recovery');
|
|
const snapshots = buildTerminalJobSnapshots({
|
|
files: [{
|
|
name: 'episode.mkv',
|
|
results: [
|
|
{ jobId: 'done-job', hoster: 'doodstream.com', status: 'done', download_url: 'https://doodstream.com/d/abc123', file_code: 'abc123' },
|
|
{ jobId: 'error-job', hoster: 'voe.sx', status: 'error', error: 'rejected', failureDetails: { kind: 'hoster' } },
|
|
{ hoster: 'byse.sx', status: 'done', download_url: 'https://byse.sx/d/no-id' }
|
|
]
|
|
}]
|
|
});
|
|
|
|
assert.deepEqual(snapshots, [
|
|
{
|
|
jobId: 'done-job',
|
|
status: 'done',
|
|
error: null,
|
|
failureDetails: null,
|
|
result: { download_url: 'https://doodstream.com/d/abc123', embed_url: null, file_code: 'abc123' }
|
|
},
|
|
{
|
|
jobId: 'error-job',
|
|
status: 'error',
|
|
error: 'rejected',
|
|
failureDetails: { kind: 'hoster' },
|
|
result: null
|
|
}
|
|
]);
|
|
});
|
|
|
|
test('recovery markers affect only their exact job IDs and never restart terminal outcomes', () => {
|
|
const { getRecoveryOutcome } = require('../lib/upload-recovery');
|
|
const recovery = {
|
|
jobIds: ['done-job', 'active-job'],
|
|
terminalJobs: [{
|
|
jobId: 'done-job',
|
|
status: 'done',
|
|
error: null,
|
|
failureDetails: null,
|
|
result: { download_url: 'https://doodstream.com/d/abc123', embed_url: null, file_code: 'abc123' }
|
|
}]
|
|
};
|
|
|
|
assert.deepEqual(getRecoveryOutcome({ id: 'done-job', status: 'preview' }, recovery), {
|
|
status: 'done',
|
|
error: null,
|
|
failureDetails: null,
|
|
result: { download_url: 'https://doodstream.com/d/abc123', embed_url: null, file_code: 'abc123' },
|
|
interrupted: false
|
|
});
|
|
assert.deepEqual(getRecoveryOutcome({ id: 'active-job', status: 'queued' }, recovery), { status: 'queued', interrupted: true });
|
|
assert.deepEqual(getRecoveryOutcome({ id: 'foreign-job', status: 'queued' }, recovery), { status: 'queued', interrupted: false });
|
|
assert.deepEqual(getRecoveryOutcome({ id: 'already-done', status: 'done' }, recovery), { status: 'done', interrupted: false });
|
|
});
|
|
|
|
test('main and renderer keep recovery evidence until final queue persistence succeeds', () => {
|
|
const root = path.join(__dirname, '..');
|
|
const mainSource = fs.readFileSync(path.join(root, 'main.js'), 'utf8');
|
|
const rendererSource = fs.readFileSync(path.join(root, 'renderer', 'app.js'), 'utf8');
|
|
const indexSource = fs.readFileSync(path.join(root, 'renderer', 'index.html'), 'utf8');
|
|
const batchDone = mainSource.slice(mainSource.indexOf("uploadManager.on('batch-done'"), mainSource.indexOf("ipcMain.handle('cancel-upload'"));
|
|
|
|
assert.match(batchDone, /buildTerminalJobSnapshots\(summary\)/);
|
|
assert.match(batchDone, /if \(queuePersisted && terminalRecoveryPersisted\)[\s\S]*saveUploadRecovery\(null\)/);
|
|
assert.ok(batchDone.indexOf('saveUploadRecovery(recoveryWithTerminalJobs)') < batchDone.indexOf('requestUploadFinalization(summary, historyPersisted)'));
|
|
const startFailure = batchDone.slice(batchDone.indexOf('startBatch(tasks'));
|
|
assert.match(startFailure, /buildFailedUploadSummary\(tasks/);
|
|
assert.match(startFailure, /saveUploadRecovery\(terminalRecovery\)/);
|
|
assert.match(startFailure, /requestUploadFinalization\(errorSummary, historyPersisted\)/);
|
|
assert.match(startFailure, /if \(queuePersisted && terminalRecoveryPersisted\)[\s\S]*saveUploadRecovery\(null\)/);
|
|
assert.match(rendererSource, /window\.UploadRecovery\.getRecoveryOutcome/);
|
|
assert.match(rendererSource, /data\.historyPersisted !== true/);
|
|
assert.ok(indexSource.indexOf('../lib/upload-recovery.js') < indexSource.indexOf('app.js'));
|
|
});
|