Files
Multi-Hoster-Upload/tests/upload-recovery.test.js
T
Sucukdeluxe b286574902
CI / verify (push) Failing after 21m12s
Harden upload finalization and release verification
Preserve uncertain remote commits and destructive source-cleanup requirements across cancellation, persistence failures, retries, and restart recovery. Revalidate account availability after admission waits and prevent late upload results from bypassing cancellation state. Strengthen release argument, CI tag, source allowlist, and secret scanning gates. Run the Electron smoke suite in a fully hidden offscreen harness that cannot reveal, focus, or elevate native windows.
2026-08-15 04:06:53 +02:00

142 lines
7.6 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' }, remoteCommitUncertain: true },
{ 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' },
remoteCommitUncertain: true,
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'],
historyPending: true,
terminalJobs: [{
jobId: 'done-job',
status: 'done',
error: null,
failureDetails: null,
result: { download_url: 'https://doodstream.com/d/abc123', embed_url: null, file_code: 'abc123' }
}, {
jobId: 'uncertain-job',
status: 'error',
error: 'remote result unknown',
failureDetails: null,
remoteCommitUncertain: true,
result: null
}]
};
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' },
historyPending: true,
interrupted: false
});
assert.deepEqual(getRecoveryOutcome({ id: 'active-job', status: 'queued' }, recovery), { status: 'queued', interrupted: true });
assert.deepEqual(getRecoveryOutcome({ id: 'uncertain-job', status: 'preview' }, recovery), {
status: 'error',
error: 'remote result unknown',
failureDetails: null,
remoteCommitUncertain: true,
result: null,
historyPending: true,
interrupted: false
});
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('remote commit uncertainty survives acknowledgements and failed retries until confirmed success', () => {
const { resolveRemoteCommitUncertainty } = require('../lib/upload-recovery');
assert.equal(resolveRemoteCommitUncertainty(true, { status: 'queued' }), true);
assert.equal(resolveRemoteCommitUncertainty(true, { status: 'getting-server' }), true);
assert.equal(resolveRemoteCommitUncertainty(true, { status: 'uploading' }), true);
assert.equal(resolveRemoteCommitUncertainty(true, { status: 'error' }), true);
assert.equal(resolveRemoteCommitUncertainty(true, { status: 'aborted' }), true);
assert.equal(resolveRemoteCommitUncertainty(true, { status: 'done' }), false);
assert.equal(resolveRemoteCommitUncertainty(false, { status: 'error', remoteCommitUncertain: true }), true);
assert.equal(resolveRemoteCommitUncertainty(false, { status: 'error' }), 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'"));
const barrier = mainSource.slice(mainSource.indexOf('function createUploadFinalizationBarrier'), mainSource.indexOf('function requestUploadFinalization'));
assert.match(mainSource, /buildTerminalSnapshots: buildTerminalJobSnapshots/);
assert.ok(barrier.indexOf('appendHistory(summary)') < barrier.indexOf('saveRecovery(terminalRecovery)'));
assert.ok(barrier.indexOf('saveRecovery(terminalRecovery)') < barrier.indexOf('requestFinalization(summary, historyPersisted)'));
assert.match(barrier, /if \(historyPersisted && queuePersisted && terminalRecoveryPersisted\)[\s\S]*saveRecovery\(null\)/);
assert.match(batchDone, /uploadFinalizationBarrier\.finalize\(summary, recovery\)/);
const startFailure = batchDone.slice(batchDone.indexOf('startBatch(tasks'));
assert.match(startFailure, /buildFailedUploadSummary\(tasks/);
assert.match(startFailure, /uploadFinalizationBarrier\.finalize\(errorSummary, recovery\)/);
const startHandler = mainSource.slice(mainSource.indexOf("ipcMain.handle('start-upload'"), mainSource.indexOf("ipcMain.handle('cancel-upload'"));
assert.match(startHandler, /await configStore\.saveUploadRecovery\(recovery\)/);
assert.match(startHandler, /uploadFinalizationBarrier\.finalize\(skippedSummary/);
assert.match(startHandler, /finalized: true/);
assert.ok(startHandler.indexOf('sourceCleanup.registerGroups(sourceCleanupGroups)') < startHandler.indexOf('saveUploadRecovery(recovery)'));
assert.match(startHandler, /catch \(error\)[\s\S]*return { error: 'Upload-Wiederherstellung konnte nicht gespeichert werden' }/);
const addHandler = mainSource.slice(mainSource.indexOf("ipcMain.handle('add-jobs-to-batch'"), mainSource.indexOf("ipcMain.handle('finish-after-active'"));
assert.match(addHandler, /await configStore\.saveUploadRecovery\(nextRecovery\)/);
assert.ok(addHandler.indexOf('saveUploadRecovery(nextRecovery)') < addHandler.indexOf('batchManager.addJobs(tasks)'));
assert.match(rendererSource, /window\.UploadRecovery\.getRecoveryOutcome/);
assert.match(rendererSource, /data\.historyPersisted !== true/);
assert.match(rendererSource, /deliveryId: data\.deliveryId/);
assert.match(rendererSource, /remoteCommitUncertain: Object\.hasOwn\(recoveryOutcome, 'remoteCommitUncertain'\)/);
assert.match(rendererSource, /remoteCommitUncertain: job\.remoteCommitUncertain === true/);
assert.match(rendererSource, /j\.remoteCommitUncertain !== true/);
assert.equal((rendererSource.match(/window\.UploadRecovery\.resolveRemoteCommitUncertainty/g) || []).length >= 2, true);
assert.doesNotMatch(rendererSource, /acknowledgeRemoteCommitRetry/);
assert.ok(indexSource.indexOf('../lib/upload-recovery.js') < indexSource.indexOf('app.js'));
});