Files
fluxer/fluxer_desktop/src/main/SplashDownloadDiagnostics.test.mjs
T

164 lines
6.1 KiB
JavaScript

// SPDX-License-Identifier: AGPL-3.0-or-later
import assert from 'node:assert/strict';
import {describe, test} from 'node:test';
import './LocalAppTestSupport.test.mjs';
const {ModuleDownloadRate} = await import('@electron/main/ModuleDownloadRate');
const {buildSplashDiagnosticsText, redactDiagnosticText} = await import('@electron/main/SplashDiagnostics');
const {isModulePackageStall, ModulePackageFetchError, ModulePackageStallError, MODULE_PACKAGE_STALL_TIMEOUT_MS} =
await import('@electron/main/ModulePackageInstaller');
const {serializeSplashState, SplashStatus} = await import('@electron/main/SplashWindow');
function diagnosticsInput(overrides = {}) {
return {
generatedAt: Date.UTC(2026, 9, 8, 1, 0, 0),
splashOpenedAt: Date.UTC(2026, 9, 8, 0, 59, 0),
appVersion: '2026.1008.1',
channel: 'canary',
platform: 'win32',
arch: 'x64',
osVersion: '10.0.26100',
electronVersion: '44.4.1',
logsPath: 'C:\\Users\\u\\AppData\\Roaming\\fluxer_desktop_canary\\logs',
userDataPath: 'C:\\Users\\u\\AppData\\Roaming\\fluxercanary',
packageOrigin: 'https://pkgs.fluxer.com',
proxyRoute: 'PROXY 10.0.0.2:3128',
splashStatus: 'download-stalled',
updaterStatus: 'retry-wait',
pendingModule: 'fluxer_renderer',
receivedBytes: 42000000,
totalBytes: 146684915,
bytesPerSecond: 3100000,
committed: {fluxer_sourcemaps: 'b'.repeat(64), fluxer_renderer: 'a'.repeat(64)},
lastError: 'package download stalled for 30000ms',
lastErrorAt: Date.UTC(2026, 9, 8, 0, 59, 40),
...overrides,
};
}
describe('module download rate', () => {
test('reports nothing until a second sample and then the transfer rate', () => {
const rate = new ModuleDownloadRate();
assert.equal(rate.sample(0, 0), null);
assert.equal(rate.sample(1_000_000, 1000), 1_000_000);
});
test('smooths a sudden change instead of jumping to it', () => {
const rate = new ModuleDownloadRate();
rate.sample(0, 0);
rate.sample(1_000_000, 1000);
const smoothed = rate.sample(1_000_000 + 4_000_000, 2000);
assert.ok(smoothed > 1_000_000 && smoothed < 4_000_000, String(smoothed));
});
test('keeps the last rate for samples closer together than the minimum window', () => {
const rate = new ModuleDownloadRate();
rate.sample(0, 0);
const first = rate.sample(1_000_000, 1000);
assert.equal(rate.sample(1_100_000, 1050), first);
});
test('starts over when the byte count goes backwards or after a reset', () => {
const rate = new ModuleDownloadRate();
rate.sample(0, 0);
rate.sample(5_000_000, 1000);
assert.equal(rate.sample(100, 1500), null);
rate.reset();
assert.equal(rate.sample(0, 3000), null);
});
});
describe('package download stalls', () => {
test('a stall is a transient fetch failure the updater can name', () => {
const stall = new ModulePackageStallError('fluxer_renderer', MODULE_PACKAGE_STALL_TIMEOUT_MS);
assert.ok(stall instanceof ModulePackageFetchError);
assert.equal(stall.status, null);
assert.equal(isModulePackageStall(new Error('install failed', {cause: stall})), true);
assert.equal(isModulePackageStall(new ModulePackageFetchError('fluxer_renderer', 503, 'busy')), false);
});
test('gives up on a silent transfer after thirty seconds', () => {
assert.equal(MODULE_PACKAGE_STALL_TIMEOUT_MS, 30000);
});
});
describe('splash download state', () => {
test('carries byte counts and speed across the IPC boundary as whole numbers', () => {
const serialized = serializeSplashState({
status: SplashStatus.DOWNLOADING_UPDATES,
receivedBytes: 42_000_000.7,
totalBytes: 146_684_915,
bytesPerSecond: 3_100_000.4,
});
assert.equal(serialized.receivedBytes, 42_000_000);
assert.equal(serialized.totalBytes, 146_684_915);
assert.equal(serialized.bytesPerSecond, 3_100_000);
});
test('keeps the stalled status and drops byte values that are not numbers', () => {
const serialized = serializeSplashState({
status: SplashStatus.DOWNLOAD_STALLED,
seconds: 4,
receivedBytes: Number.NaN,
totalBytes: '9',
bytesPerSecond: Number.POSITIVE_INFINITY,
});
assert.equal(serialized.status, 'download-stalled');
assert.equal(serialized.receivedBytes, null);
assert.equal(serialized.totalBytes, null);
assert.equal(serialized.bytesPerSecond, null);
});
});
describe('splash diagnostics', () => {
test('lists what support needs to place a stuck download', () => {
const text = buildSplashDiagnosticsText(diagnosticsInput());
for (const expected of [
'App version: 2026.1008.1 (canary)',
'Platform: win32 x64, OS 10.0.26100, Electron 44.4.1',
'Package origin: https://pkgs.fluxer.com',
'Proxy route: PROXY 10.0.0.2:3128',
'Splash status: download-stalled',
'Pending module: fluxer_renderer',
'Downloaded: 42000000 bytes of 146684915 bytes',
'Speed: 3100000 bytes/s',
'Last updater error: package download stalled for 30000ms',
'Last updater error at: 2026-10-08T00:59:40.000Z',
' fluxer_renderer aaaaaaaaaaaa',
' fluxer_sourcemaps bbbbbbbbbbbb',
]) {
assert.ok(text.includes(expected), `missing ${expected}\n${text}`);
}
assert.ok(text.indexOf('fluxer_renderer aaaa') < text.indexOf('fluxer_sourcemaps bbbb'));
});
test('never carries query values or credential-looking pairs', () => {
const text = buildSplashDiagnosticsText(
diagnosticsInput({
lastError: 'failed to reach https://pkgs.fluxer.com/x?token=abc123&sig=zzz: authorization: Bearer secretvalue',
proxyRoute: 'PROXY user:pass@proxy:8080',
}),
);
assert.ok(!text.includes('abc123'));
assert.ok(!text.includes('zzz'));
assert.ok(!text.includes('secretvalue'));
assert.ok(!text.includes('user:pass'));
assert.ok(text.includes('?token=…&sig=…'));
assert.ok(text.includes('Proxy route: PROXY …@proxy:8080'));
});
test('redaction leaves plain diagnostic text alone', () => {
assert.equal(
redactDiagnosticText('Package origin: https://pkgs.fluxer.com'),
'Package origin: https://pkgs.fluxer.com',
);
});
test('says so when nothing is installed yet', () => {
const text = buildSplashDiagnosticsText(diagnosticsInput({committed: {}, lastError: null, lastErrorAt: null}));
assert.ok(text.includes('Committed modules:\n none'));
assert.ok(text.includes('Last updater error: none'));
});
});