fix(downloads): recover stalls and honor connection defaults (#19, #20)

This commit is contained in:
NimBold
2026-07-16 14:28:59 +03:30
parent edeef0ac54
commit f3d0e0be13
7 changed files with 555 additions and 85 deletions
+15 -1
View File
@@ -1,6 +1,6 @@
import { describe, expect, it } from 'vitest';
import type { DownloadItem } from '../bindings/DownloadItem';
import { redactDownloadForPersistence } from './downloads';
import { redactDownloadForPersistence, resolveDownloadConnections } from './downloads';
const item = (status: DownloadItem['status']): DownloadItem => ({
id: 'download-1',
@@ -42,3 +42,17 @@ describe('download persistence progress snapshots', () => {
}
);
});
describe('download connection resolution', () => {
it('uses a clamped fallback for legacy rows without a saved value', () => {
expect(resolveDownloadConnections(undefined, 8)).toBe(8);
expect(resolveDownloadConnections(undefined, 0)).toBe(1);
expect(resolveDownloadConnections(undefined, Number.NaN)).toBe(16);
});
it('clamps malformed saved values before dispatch', () => {
expect(resolveDownloadConnections(0, 8)).toBe(1);
expect(resolveDownloadConnections(17, 8)).toBe(16);
expect(resolveDownloadConnections(Number.NaN, 8)).toBe(8);
});
});
+31
View File
@@ -40,6 +40,37 @@ export const isActiveDownloadStatus = (status: DownloadStatus): boolean =>
export const isTransferActiveStatus = (status: DownloadStatus): boolean =>
status === 'downloading' || status === 'processing' || status === 'retrying';
export const DOWNLOAD_CONNECTIONS_MIN = 1;
export const DOWNLOAD_CONNECTIONS_MAX = 16;
/**
* Resolve persisted/user-entered connection values before they cross into the
* backend. Older rows may omit the value, while malformed rows can contain
* zero, NaN, or an out-of-range number.
*/
export const resolveDownloadConnections = (value: unknown, fallback: unknown): number => {
const toFiniteInteger = (candidate: unknown): number | undefined => {
if (typeof candidate === 'number') {
return Number.isFinite(candidate) ? Math.trunc(candidate) : undefined;
}
if (typeof candidate === 'string' && candidate.trim() !== '') {
const parsed = Number(candidate);
return Number.isFinite(parsed) ? Math.trunc(parsed) : undefined;
}
return undefined;
};
const normalizedFallback = toFiniteInteger(fallback) ?? DOWNLOAD_CONNECTIONS_MAX;
const safeFallback = Math.min(
DOWNLOAD_CONNECTIONS_MAX,
Math.max(DOWNLOAD_CONNECTIONS_MIN, normalizedFallback)
);
const candidate = toFiniteInteger(value) ?? safeFallback;
return Math.min(
DOWNLOAD_CONNECTIONS_MAX,
Math.max(DOWNLOAD_CONNECTIONS_MIN, candidate)
);
};
export const normalizeSpeedLimitForBackend = (value?: string | null): string | null => {
const trimmed = value?.trim();
if (!trimmed) return null;