From 4d0b58285fd424ed6055281d75e896175d731d0f Mon Sep 17 00:00:00 2001 From: Sucukdeluxe <259325684+Sucukdeluxe@users.noreply.github.com> Date: Wed, 26 Aug 2026 09:02:19 +0200 Subject: [PATCH] fix: clamp fractional automation queue limits --- lib/automation-control.js | 4 ++-- tests/automation-control.test.js | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/lib/automation-control.js b/lib/automation-control.js index fbd4578..a9f8e49 100644 --- a/lib/automation-control.js +++ b/lib/automation-control.js @@ -21,8 +21,8 @@ if (isString && value.trim() === '') return 15000; const number = Number(value); if (!Number.isFinite(number) || number < 0) return 15000; - if (number === 0 && isString && value.trim() !== '0') return 15000; - return Math.floor(number); + if (number === 0) return isString && value.trim() !== '0' ? 15000 : 0; + return Math.max(1, Math.floor(number)); } function normalizeAutomationSettings(value = {}) { diff --git a/tests/automation-control.test.js b/tests/automation-control.test.js index ecd2fd2..d89ba2d 100644 --- a/tests/automation-control.test.js +++ b/tests/automation-control.test.js @@ -59,6 +59,13 @@ test('automation settings allow only numeric and trimmed string zero to disable assert.equal(normalizeAutomationSettings({ queueLimitJobs: ' 0 ' }).queueLimitJobs, 0); }); +test('automation settings clamp positive queue limit fractions to one', () => { + assert.deepEqual( + [0.5, '0.5', 0.125].map((queueLimitJobs) => normalizeAutomationSettings({ queueLimitJobs }).queueLimitJobs), + [1, 1, 1] + ); +}); + test('capacity counts only executable and running queue jobs', () => { const statuses = ['preview', 'queued', 'getting-server', 'uploading', 'retrying', 'done', 'error', 'aborted', 'skipped']; assert.equal(countAutomaticQueueJobs(statuses.map((status, index) => ({ id: String(index), status }))), 5); @@ -146,6 +153,20 @@ test('admission allows only numeric and trimmed string zero to disable the queue ); }); +test('admission keeps positive queue limit fractions bounded to one job', () => { + const candidate = { path: 'two-jobs.mkv', mtimeMs: 1, eligibleJobCount: 2 }; + assert.deepEqual( + [0.5, '0.5', 0.125].map((queueLimitJobs) => planAtomicAdmissions({ candidates: [candidate], queueLimitJobs })), + [0.5, '0.5', 0.125].map(() => ({ + admittedPaths: [], + deferredPaths: ['two-jobs.mkv'], + currentJobCount: 0, + plannedJobs: 0, + availableSlots: 1 + })) + ); +}); + test('daily telemetry resets atomically on the local calendar day boundary', () => { const before = { dateKey: '2026-08-25', detected: 8, queued: 4, skipped: 2, deferred: 1 }; const now = new Date(2026, 7, 26, 0, 0, 1).getTime();