fix: isolate valid add payload identities
Validate every add candidate against the complete queue identity snapshot before renderer IPC submission. Build source cleanup groups only from globally unique, non-empty job IDs and derive required hosters from those same valid participants. Reuse the frozen request analysis when reconciling Main responses so ambiguous jobs remain local with exact cleanup rollback while unique siblings can still be confirmed. Cover active selection, manual modal injection, and automation injection with hidden renderer regressions for jobs, cleanup groups, required hosters, and misleading confirmations.
This commit is contained in:
+50
-10
@@ -732,7 +732,10 @@ async function applyAutomationEvaluation(evaluation) {
|
|||||||
const error = sanitizeUploadControlError(result.error, 'Jobs konnten nicht hinzugefügt werden.');
|
const error = sanitizeUploadControlError(result.error, 'Jobs konnten nicht hinzugefügt werden.');
|
||||||
return freezeAutomationValue({ ok: false, error, warning: null, admittedFiles: [], deferredFiles, paused: /pausiert/i.test(error), dryRun: false });
|
return freezeAutomationValue({ ok: false, error, warning: null, admittedFiles: [], deferredFiles, paused: /pausiert/i.test(error), dryRun: false });
|
||||||
}
|
}
|
||||||
const outcome = applyAddJobsOutcome(newJobs, result, { cleanupStates: cleanupPreparation.rollbackStates });
|
const outcome = applyAddJobsOutcome(newJobs, result, {
|
||||||
|
analysis: addRequest,
|
||||||
|
cleanupStates: cleanupPreparation.rollbackStates
|
||||||
|
});
|
||||||
if (!outcome.consistent) {
|
if (!outcome.consistent) {
|
||||||
renderQueueTable();
|
renderQueueTable();
|
||||||
persistQueueStateSoon(true);
|
persistQueueStateSoon(true);
|
||||||
@@ -1535,7 +1538,10 @@ async function applyHosterSelection() {
|
|||||||
regularInjectionFailure = sanitizeUploadControlError(result.error, 'Jobs konnten nicht hinzugefügt werden.');
|
regularInjectionFailure = sanitizeUploadControlError(result.error, 'Jobs konnten nicht hinzugefügt werden.');
|
||||||
restoreSourceCleanupStates(cleanupPreparation.rollbackStates);
|
restoreSourceCleanupStates(cleanupPreparation.rollbackStates);
|
||||||
} else {
|
} else {
|
||||||
const outcome = applyAddJobsOutcome(newJobs, result, { cleanupStates: cleanupPreparation.rollbackStates });
|
const outcome = applyAddJobsOutcome(newJobs, result, {
|
||||||
|
analysis: addRequest,
|
||||||
|
cleanupStates: cleanupPreparation.rollbackStates
|
||||||
|
});
|
||||||
if (!outcome.consistent) regularInjectionFailure = 'Jobs konnten nicht eindeutig bestätigt werden.';
|
if (!outcome.consistent) regularInjectionFailure = 'Jobs konnten nicht eindeutig bestätigt werden.';
|
||||||
else if (result?.sourceCleanupFingerprints && window.SourceCleanupPolicy) window.SourceCleanupPolicy.applyFingerprints(queueJobs, result.sourceCleanupFingerprints);
|
else if (result?.sourceCleanupFingerprints && window.SourceCleanupPolicy) window.SourceCleanupPolicy.applyFingerprints(queueJobs, result.sourceCleanupFingerprints);
|
||||||
}
|
}
|
||||||
@@ -3785,14 +3791,22 @@ function sanitizeUploadControlError(error, fallback) {
|
|||||||
return /pausiert/i.test(message) ? 'Automatik ist pausiert' : fallback;
|
return /pausiert/i.test(message) ? 'Automatik ist pausiert' : fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
function analyzeAddJobsInput(jobs) {
|
function analyzeAddJobsInput(jobs, relevantJobs = jobs) {
|
||||||
const inputJobs = Array.isArray(jobs) ? jobs : [];
|
const inputJobs = Array.isArray(jobs) ? jobs : [];
|
||||||
|
const identityJobs = Array.isArray(relevantJobs) ? relevantJobs : [];
|
||||||
const idCounts = new Map();
|
const idCounts = new Map();
|
||||||
for (const job of inputJobs) {
|
for (const job of identityJobs) {
|
||||||
const id = job?.id;
|
const id = job?.id;
|
||||||
if (typeof id !== 'string' || id.trim().length === 0) continue;
|
if (typeof id !== 'string' || id.trim().length === 0) continue;
|
||||||
idCounts.set(id, (idCounts.get(id) || 0) + 1);
|
idCounts.set(id, (idCounts.get(id) || 0) + 1);
|
||||||
}
|
}
|
||||||
|
const confirmableJobs = [];
|
||||||
|
const confirmableJobsById = new Map();
|
||||||
|
for (const job of identityJobs) {
|
||||||
|
if (typeof job?.id !== 'string' || job.id.trim().length === 0 || idCounts.get(job.id) !== 1) continue;
|
||||||
|
confirmableJobs.push(job);
|
||||||
|
confirmableJobsById.set(job.id, job);
|
||||||
|
}
|
||||||
const sendableJobs = [];
|
const sendableJobs = [];
|
||||||
const unconfirmableJobs = [];
|
const unconfirmableJobs = [];
|
||||||
const jobsById = new Map();
|
const jobsById = new Map();
|
||||||
@@ -3804,13 +3818,38 @@ function analyzeAddJobsInput(jobs) {
|
|||||||
unconfirmableJobs.push(job);
|
unconfirmableJobs.push(job);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { inputJobs, sendableJobs, unconfirmableJobs, jobsById };
|
return { inputJobs, sendableJobs, unconfirmableJobs, jobsById, confirmableJobs, confirmableJobsById };
|
||||||
|
}
|
||||||
|
|
||||||
|
function prepareAddSourceCleanup(analysis) {
|
||||||
|
const rollbackStates = captureSourceCleanupStates();
|
||||||
|
if (!config.globalSettings?.deleteSourceAfterSuccessfulUpload || !window.SourceCleanupPolicy) return { groups: [], rollbackStates };
|
||||||
|
const cleanupJobs = analysis.confirmableJobs.map(job => ({
|
||||||
|
...job,
|
||||||
|
sourceCleanupRequiredHosters: []
|
||||||
|
}));
|
||||||
|
const cleanupJobsById = new Map(cleanupJobs.map(job => [job.id, job]));
|
||||||
|
const prepared = window.SourceCleanupPolicy.prepareGroups(
|
||||||
|
cleanupJobs,
|
||||||
|
analysis.sendableJobs.map(job => cleanupJobsById.get(job.id)).filter(Boolean),
|
||||||
|
() => window.crypto.randomUUID(),
|
||||||
|
'win32'
|
||||||
|
);
|
||||||
|
for (const cleanupJob of prepared.touchedJobs) {
|
||||||
|
const job = analysis.confirmableJobsById.get(cleanupJob.id);
|
||||||
|
if (!job) continue;
|
||||||
|
for (const field of sourceCleanupFields) {
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(cleanupJob, field)) delete job[field];
|
||||||
|
else job[field] = cloneSourceCleanupValue(cleanupJob[field]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { ...prepared, rollbackStates };
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAddJobsRequest(jobs) {
|
function createAddJobsRequest(jobs) {
|
||||||
const analysis = analyzeAddJobsInput(jobs);
|
const analysis = analyzeAddJobsInput(jobs, queueJobs.slice());
|
||||||
const cleanupPreparation = analysis.sendableJobs.length > 0
|
const cleanupPreparation = analysis.sendableJobs.length > 0
|
||||||
? prepareSourceCleanup(analysis.sendableJobs)
|
? prepareAddSourceCleanup(analysis)
|
||||||
: { groups: [], rollbackStates: [] };
|
: { groups: [], rollbackStates: [] };
|
||||||
return {
|
return {
|
||||||
...analysis,
|
...analysis,
|
||||||
@@ -3827,8 +3866,8 @@ async function submitAddJobsRequest(request) {
|
|||||||
return window.api.addJobsToBatch(request.payload);
|
return window.api.addJobsToBatch(request.payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveAddJobsOutcome(jobs, result) {
|
function resolveAddJobsOutcome(jobs, result, analysis = null) {
|
||||||
const { sendableJobs, unconfirmableJobs, jobsById } = analyzeAddJobsInput(jobs);
|
const { sendableJobs, unconfirmableJobs, jobsById } = analysis || analyzeAddJobsInput(jobs);
|
||||||
const skippedEntries = [];
|
const skippedEntries = [];
|
||||||
const skippedIds = new Set();
|
const skippedIds = new Set();
|
||||||
const alreadyIds = new Set();
|
const alreadyIds = new Set();
|
||||||
@@ -3864,7 +3903,7 @@ function resolveAddJobsOutcome(jobs, result) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function applyAddJobsOutcome(jobs, result, options = {}) {
|
function applyAddJobsOutcome(jobs, result, options = {}) {
|
||||||
const outcome = resolveAddJobsOutcome(jobs, result);
|
const outcome = resolveAddJobsOutcome(jobs, result, options.analysis);
|
||||||
const confirmedJobs = new Set([...outcome.addedJobs, ...outcome.alreadyJobs, ...outcome.skippedJobs]);
|
const confirmedJobs = new Set([...outcome.addedJobs, ...outcome.alreadyJobs, ...outcome.skippedJobs]);
|
||||||
const cleanupRollbackJobs = outcome.consistent
|
const cleanupRollbackJobs = outcome.consistent
|
||||||
? []
|
? []
|
||||||
@@ -4036,6 +4075,7 @@ async function startSelectedUpload(explicitJobs) {
|
|||||||
return { ok: false, error };
|
return { ok: false, error };
|
||||||
}
|
}
|
||||||
const outcome = applyAddJobsOutcome(addable, result, {
|
const outcome = applyAddJobsOutcome(addable, result, {
|
||||||
|
analysis: addRequest,
|
||||||
cleanupStates: cleanupPreparation.rollbackStates,
|
cleanupStates: cleanupPreparation.rollbackStates,
|
||||||
uploadStates: originalStates
|
uploadStates: originalStates
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -253,7 +253,19 @@ contextBridge.exposeInMainWorld('api', {
|
|||||||
},
|
},
|
||||||
addJobsToBatch(payload) {
|
addJobsToBatch(payload) {
|
||||||
folderMonitorProbeCalls.push(['inject', payload?.jobs?.length || 0]);
|
folderMonitorProbeCalls.push(['inject', payload?.jobs?.length || 0]);
|
||||||
automationProbe.mutationCalls.push(['inject', payload?.jobs?.length || 0, (payload?.jobs || []).map(job => job.id)]);
|
automationProbe.mutationCalls.push([
|
||||||
|
'inject',
|
||||||
|
payload?.jobs?.length || 0,
|
||||||
|
(payload?.jobs || []).map(job => job.id),
|
||||||
|
(payload?.jobs || []).map(job => ({
|
||||||
|
id: job.id,
|
||||||
|
requiredHosters: [...(job.sourceCleanupRequiredHosters || [])]
|
||||||
|
})),
|
||||||
|
(payload?.sourceCleanupGroups || []).map(group => ({
|
||||||
|
requiredHosters: [...(group.requiredHosters || [])],
|
||||||
|
jobIds: (group.jobs || []).map(job => job.jobId)
|
||||||
|
}))
|
||||||
|
]);
|
||||||
if (automationProbe.addError) return Promise.reject(new Error(automationProbe.addError));
|
if (automationProbe.addError) return Promise.reject(new Error(automationProbe.addError));
|
||||||
if (automationProbe.addMode === 'partial-consistent' && payload?.jobs?.length >= 4) {
|
if (automationProbe.addMode === 'partial-consistent' && payload?.jobs?.length >= 4) {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
@@ -1212,34 +1224,50 @@ contextBridge.exposeInMainWorld('api', {
|
|||||||
job.sourceCleanupFingerprint = { index, nested: ['before-' + index] };
|
job.sourceCleanupFingerprint = { index, nested: ['before-' + index] };
|
||||||
return job;
|
return job;
|
||||||
};
|
};
|
||||||
const summarizeCollisionPath = async (jobs, result, before) => {
|
const summarizeCollisionPath = async (jobs, invalidJobs, result, before) => {
|
||||||
const probe = await window.api.getAutomationProbeState();
|
const probe = await window.api.getAutomationProbeState();
|
||||||
const inject = probe.mutationCalls.find(call => call[0] === 'inject');
|
const inject = probe.mutationCalls.find(call => call[0] === 'inject');
|
||||||
return {
|
return {
|
||||||
result,
|
result,
|
||||||
sentIds: inject?.[2] || [],
|
sentIds: inject?.[2] || [],
|
||||||
|
sentJobs: inject?.[3] || [],
|
||||||
|
sourceCleanupGroups: inject?.[4] || [],
|
||||||
statuses: jobs.map(job => job.status),
|
statuses: jobs.map(job => job.status),
|
||||||
invalidRestored: jobs.slice(0, 3).map((job, index) => JSON.stringify(job) === before[index])
|
invalidRestored: invalidJobs.map((job, index) => JSON.stringify(job) === before[index])
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
configureAtomicState(0);
|
configureAtomicState(0);
|
||||||
config.globalSettings.deleteSourceAfterSuccessfulUpload = true;
|
config.globalSettings.deleteSourceAfterSuccessfulUpload = true;
|
||||||
uploading = true;
|
uploading = true;
|
||||||
|
const activeCollisionFile = 'C:\\\\collision\\\\active.mkv';
|
||||||
const activeMissingJob = makePauseRaceJob('active-missing.mkv');
|
const activeMissingJob = makePauseRaceJob('active-missing.mkv');
|
||||||
delete activeMissingJob.id;
|
delete activeMissingJob.id;
|
||||||
const activeCollisionJobs = [
|
const activeCollisionJobs = [
|
||||||
{ ...makePauseRaceJob('active-duplicate-a.mkv'), id: 'active-duplicate' },
|
{ ...makePauseRaceJob('active-duplicate-a.mkv'), id: 'active-duplicate' },
|
||||||
{ ...makePauseRaceJob('active-duplicate-b.mkv'), id: 'active-duplicate' },
|
{ ...makePauseRaceJob('active-duplicate-b.mkv'), id: 'active-duplicate' },
|
||||||
activeMissingJob,
|
activeMissingJob,
|
||||||
|
{ ...makePauseRaceJob('active-shadowed.mkv'), id: 'active-shadowed' },
|
||||||
{ ...makePauseRaceJob('active-unique.mkv'), id: 'active-unique' }
|
{ ...makePauseRaceJob('active-unique.mkv'), id: 'active-unique' }
|
||||||
].map(applyCollisionCleanupFixture);
|
].map((job, index) => {
|
||||||
queueJobs = activeCollisionJobs;
|
job.file = activeCollisionFile;
|
||||||
|
job.hoster = [hosters[0], hosters[1], hosters[2], hosters[0], hosters[3]][index];
|
||||||
|
return applyCollisionCleanupFixture(job, index);
|
||||||
|
});
|
||||||
|
const activeShadowSibling = applyCollisionCleanupFixture({
|
||||||
|
...makePauseRaceJob('active-shadow-sibling.mkv'),
|
||||||
|
id: 'active-shadowed',
|
||||||
|
file: activeCollisionFile,
|
||||||
|
hoster: hosters[1],
|
||||||
|
status: 'done'
|
||||||
|
}, 5);
|
||||||
|
queueJobs = [...activeCollisionJobs, activeShadowSibling];
|
||||||
rebuildJobIndex();
|
rebuildJobIndex();
|
||||||
const activeCollisionBefore = activeCollisionJobs.slice(0, 3).map(job => JSON.stringify(job));
|
const activeInvalidJobs = [...activeCollisionJobs.slice(0, 4), activeShadowSibling];
|
||||||
|
const activeCollisionBefore = activeInvalidJobs.map(job => JSON.stringify(job));
|
||||||
window.api.configureAutomationProbe({ paused: false, addResult: { added: 1 } });
|
window.api.configureAutomationProbe({ paused: false, addResult: { added: 1 } });
|
||||||
const activeCollisionResult = await startSelectedUpload(activeCollisionJobs);
|
const activeCollisionResult = await startSelectedUpload(activeCollisionJobs);
|
||||||
const activeCollision = await summarizeCollisionPath(activeCollisionJobs, activeCollisionResult, activeCollisionBefore);
|
const activeCollision = await summarizeCollisionPath(activeCollisionJobs, activeInvalidJobs, activeCollisionResult, activeCollisionBefore);
|
||||||
|
|
||||||
configureAtomicState(0);
|
configureAtomicState(0);
|
||||||
config.globalSettings.deleteSourceAfterSuccessfulUpload = true;
|
config.globalSettings.deleteSourceAfterSuccessfulUpload = true;
|
||||||
@@ -1260,22 +1288,39 @@ contextBridge.exposeInMainWorld('api', {
|
|||||||
document.getElementById('hosterModalList').replaceChildren(...manualCollisionInputs);
|
document.getElementById('hosterModalList').replaceChildren(...manualCollisionInputs);
|
||||||
const originalBuildQueuePreviewForCollision = buildQueuePreview;
|
const originalBuildQueuePreviewForCollision = buildQueuePreview;
|
||||||
let manualCollisionJobs = [];
|
let manualCollisionJobs = [];
|
||||||
|
let manualInvalidJobs = [];
|
||||||
let manualCollisionBefore = [];
|
let manualCollisionBefore = [];
|
||||||
buildQueuePreview = () => {
|
buildQueuePreview = () => {
|
||||||
originalBuildQueuePreviewForCollision();
|
originalBuildQueuePreviewForCollision();
|
||||||
manualCollisionJobs = queueJobs.filter(job => job.file === manualCollisionFile.path);
|
manualCollisionJobs = queueJobs.filter(job => job.file === manualCollisionFile.path);
|
||||||
manualCollisionJobs[0].id = 'manual-duplicate';
|
manualCollisionJobs[0].id = 'manual-duplicate';
|
||||||
manualCollisionJobs[1].id = 'manual-duplicate';
|
manualCollisionJobs[1].id = 'manual-duplicate';
|
||||||
delete manualCollisionJobs[2].id;
|
manualCollisionJobs[2].id = 'manual-shadowed';
|
||||||
manualCollisionJobs[3].id = 'manual-unique';
|
manualCollisionJobs[3].id = 'manual-unique';
|
||||||
manualCollisionJobs.forEach(applyCollisionCleanupFixture);
|
manualCollisionJobs.forEach(applyCollisionCleanupFixture);
|
||||||
manualCollisionBefore = manualCollisionJobs.slice(0, 3).map(job => JSON.stringify(job));
|
const manualShadowSibling = applyCollisionCleanupFixture({
|
||||||
|
...makePauseRaceJob('manual-shadow-sibling.mkv'),
|
||||||
|
id: 'manual-shadowed',
|
||||||
|
file: manualCollisionFile.path,
|
||||||
|
hoster: hosters[0],
|
||||||
|
status: 'done'
|
||||||
|
}, 4);
|
||||||
|
const manualMissingSibling = applyCollisionCleanupFixture({
|
||||||
|
...makePauseRaceJob('manual-missing-sibling.mkv'),
|
||||||
|
file: manualCollisionFile.path,
|
||||||
|
hoster: hosters[1],
|
||||||
|
status: 'done'
|
||||||
|
}, 5);
|
||||||
|
delete manualMissingSibling.id;
|
||||||
|
queueJobs.push(manualShadowSibling, manualMissingSibling);
|
||||||
|
manualInvalidJobs = [...manualCollisionJobs.slice(0, 3), manualShadowSibling, manualMissingSibling];
|
||||||
|
manualCollisionBefore = manualInvalidJobs.map(job => JSON.stringify(job));
|
||||||
rebuildJobIndex();
|
rebuildJobIndex();
|
||||||
};
|
};
|
||||||
window.api.configureAutomationProbe({ paused: false, addResult: { added: 1 } });
|
window.api.configureAutomationProbe({ paused: false, addResult: { added: 1 } });
|
||||||
const manualCollisionResult = await applyHosterSelection();
|
const manualCollisionResult = await applyHosterSelection();
|
||||||
buildQueuePreview = originalBuildQueuePreviewForCollision;
|
buildQueuePreview = originalBuildQueuePreviewForCollision;
|
||||||
const manualCollision = await summarizeCollisionPath(manualCollisionJobs, manualCollisionResult, manualCollisionBefore);
|
const manualCollision = await summarizeCollisionPath(manualCollisionJobs, manualInvalidJobs, manualCollisionResult, manualCollisionBefore);
|
||||||
|
|
||||||
configureAtomicState(0);
|
configureAtomicState(0);
|
||||||
config.globalSettings.deleteSourceAfterSuccessfulUpload = true;
|
config.globalSettings.deleteSourceAfterSuccessfulUpload = true;
|
||||||
@@ -1284,6 +1329,25 @@ contextBridge.exposeInMainWorld('api', {
|
|||||||
selectedFiles = [];
|
selectedFiles = [];
|
||||||
uploading = true;
|
uploading = true;
|
||||||
const automationCollisionFile = { path: 'C:\\collision\\automation.mkv', name: 'automation.mkv', size: 1, mtimeMs: 1 };
|
const automationCollisionFile = { path: 'C:\\collision\\automation.mkv', name: 'automation.mkv', size: 1, mtimeMs: 1 };
|
||||||
|
const automationShadowSibling = applyCollisionCleanupFixture({
|
||||||
|
...makePauseRaceJob('automation-shadow-sibling.mkv'),
|
||||||
|
id: 'automation-shadowed',
|
||||||
|
file: 'C:\\collision\\automation-shadow-existing.mkv',
|
||||||
|
hoster: hosters[0],
|
||||||
|
status: 'done'
|
||||||
|
}, 4);
|
||||||
|
const automationMissingSibling = applyCollisionCleanupFixture({
|
||||||
|
...makePauseRaceJob('automation-missing-sibling.mkv'),
|
||||||
|
file: 'C:\\collision\\automation-missing-existing.mkv',
|
||||||
|
hoster: hosters[1],
|
||||||
|
status: 'done'
|
||||||
|
}, 5);
|
||||||
|
delete automationMissingSibling.id;
|
||||||
|
automationShadowSibling.sourceCleanupToken = 'automation-collision-token';
|
||||||
|
automationMissingSibling.sourceCleanupToken = 'automation-collision-token';
|
||||||
|
queueJobs.push(automationShadowSibling, automationMissingSibling);
|
||||||
|
rebuildJobIndex();
|
||||||
|
const automationExternalBefore = [automationShadowSibling, automationMissingSibling].map(job => JSON.stringify(job));
|
||||||
const originalCreateAutomationPreviewJobForCollision = createAutomationPreviewJob;
|
const originalCreateAutomationPreviewJobForCollision = createAutomationPreviewJob;
|
||||||
const automationCollisionJobs = [];
|
const automationCollisionJobs = [];
|
||||||
const automationCollisionBefore = [];
|
const automationCollisionBefore = [];
|
||||||
@@ -1291,9 +1355,10 @@ contextBridge.exposeInMainWorld('api', {
|
|||||||
const job = originalCreateAutomationPreviewJobForCollision(file, hoster);
|
const job = originalCreateAutomationPreviewJobForCollision(file, hoster);
|
||||||
const index = automationCollisionJobs.length;
|
const index = automationCollisionJobs.length;
|
||||||
if (index < 2) job.id = 'automation-duplicate';
|
if (index < 2) job.id = 'automation-duplicate';
|
||||||
else if (index === 2) delete job.id;
|
else if (index === 2) job.id = 'automation-shadowed';
|
||||||
else job.id = 'automation-unique';
|
else job.id = 'automation-unique';
|
||||||
applyCollisionCleanupFixture(job, index);
|
applyCollisionCleanupFixture(job, index);
|
||||||
|
job.sourceCleanupToken = 'automation-collision-token';
|
||||||
automationCollisionJobs.push(job);
|
automationCollisionJobs.push(job);
|
||||||
if (index < 3) automationCollisionBefore.push(JSON.stringify(job));
|
if (index < 3) automationCollisionBefore.push(JSON.stringify(job));
|
||||||
return job;
|
return job;
|
||||||
@@ -1302,11 +1367,12 @@ contextBridge.exposeInMainWorld('api', {
|
|||||||
const automationCollisionEvaluation = await evaluateAutomationCandidates([automationCollisionFile], { dryRun: false, trigger: 'watcher' });
|
const automationCollisionEvaluation = await evaluateAutomationCandidates([automationCollisionFile], { dryRun: false, trigger: 'watcher' });
|
||||||
const automationCollisionResult = await applyAutomationEvaluation(automationCollisionEvaluation);
|
const automationCollisionResult = await applyAutomationEvaluation(automationCollisionEvaluation);
|
||||||
createAutomationPreviewJob = originalCreateAutomationPreviewJobForCollision;
|
createAutomationPreviewJob = originalCreateAutomationPreviewJobForCollision;
|
||||||
const automationCollision = await summarizeCollisionPath(automationCollisionJobs, {
|
const automationInvalidJobs = [...automationCollisionJobs.slice(0, 3), automationShadowSibling, automationMissingSibling];
|
||||||
|
const automationCollision = await summarizeCollisionPath(automationCollisionJobs, automationInvalidJobs, {
|
||||||
ok: automationCollisionResult.ok,
|
ok: automationCollisionResult.ok,
|
||||||
error: automationCollisionResult.error,
|
error: automationCollisionResult.error,
|
||||||
admitted: automationCollisionResult.admittedFiles.map(file => file.name)
|
admitted: automationCollisionResult.admittedFiles.map(file => file.name)
|
||||||
}, automationCollisionBefore);
|
}, [...automationCollisionBefore, ...automationExternalBefore]);
|
||||||
uploading = false;
|
uploading = false;
|
||||||
const collisionAdmission = { active: activeCollision, manual: manualCollision, automation: automationCollision };
|
const collisionAdmission = { active: activeCollision, manual: manualCollision, automation: automationCollision };
|
||||||
|
|
||||||
@@ -2101,20 +2167,26 @@ app.whenReady().then(async () => {
|
|||||||
active: {
|
active: {
|
||||||
result: { ok: false, error: 'Jobs konnten nicht eindeutig bestätigt werden.' },
|
result: { ok: false, error: 'Jobs konnten nicht eindeutig bestätigt werden.' },
|
||||||
sentIds: ['active-unique'],
|
sentIds: ['active-unique'],
|
||||||
statuses: ['preview', 'preview', 'preview', 'queued'],
|
sentJobs: [{ id: 'active-unique', requiredHosters: ['byse.sx'] }],
|
||||||
invalidRestored: [true, true, true]
|
sourceCleanupGroups: [{ requiredHosters: ['byse.sx'], jobIds: ['active-unique'] }],
|
||||||
|
statuses: ['preview', 'preview', 'preview', 'preview', 'queued'],
|
||||||
|
invalidRestored: [true, true, true, true, true]
|
||||||
},
|
},
|
||||||
manual: {
|
manual: {
|
||||||
result: { ok: false, error: 'Jobs konnten nicht eindeutig bestätigt werden.' },
|
result: { ok: false, error: 'Jobs konnten nicht eindeutig bestätigt werden.' },
|
||||||
sentIds: ['manual-unique'],
|
sentIds: ['manual-unique'],
|
||||||
|
sentJobs: [{ id: 'manual-unique', requiredHosters: ['byse.sx'] }],
|
||||||
|
sourceCleanupGroups: [{ requiredHosters: ['byse.sx'], jobIds: ['manual-unique'] }],
|
||||||
statuses: ['preview', 'preview', 'preview', 'queued'],
|
statuses: ['preview', 'preview', 'preview', 'queued'],
|
||||||
invalidRestored: [true, true, true]
|
invalidRestored: [true, true, true, true, true]
|
||||||
},
|
},
|
||||||
automation: {
|
automation: {
|
||||||
result: { ok: false, error: 'Jobs konnten nicht eindeutig bestätigt werden.', admitted: [] },
|
result: { ok: false, error: 'Jobs konnten nicht eindeutig bestätigt werden.', admitted: [] },
|
||||||
sentIds: ['automation-unique'],
|
sentIds: ['automation-unique'],
|
||||||
|
sentJobs: [{ id: 'automation-unique', requiredHosters: ['byse.sx'] }],
|
||||||
|
sourceCleanupGroups: [{ requiredHosters: ['byse.sx'], jobIds: ['automation-unique'] }],
|
||||||
statuses: ['preview', 'preview', 'preview', 'queued'],
|
statuses: ['preview', 'preview', 'preview', 'queued'],
|
||||||
invalidRestored: [true, true, true]
|
invalidRestored: [true, true, true, true, true]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
assert.deepEqual(result.automationPipeline.pauseBetweenApplyAndStart, {
|
assert.deepEqual(result.automationPipeline.pauseBetweenApplyAndStart, {
|
||||||
|
|||||||
Reference in New Issue
Block a user