Files
BetterDesk/web-nodejs/tests/updateService.consoleSync.test.js
T
UNITRONIX 29d03492fb fix(installer): harden cross-platform lifecycle reliability
Harden native, agent and Docker lifecycle paths with tracked update verification, rollback and preflight checks, safe uninstall semantics, and health verification. Add shared protocol tests, installer CI gates, and lifecycle documentation.

Thanks: INSOLVE (Honorary); Marco Jakobs (@jacotec); MyNameisStitch (@MyNameisStitch); Redspin (@playerumpknow)
2026-08-15 21:50:47 +02:00

111 lines
4.9 KiB
JavaScript

'use strict';
const fs = require('fs');
const os = require('os');
const path = require('path');
const { createConsoleDeployGraph } = require('../lib/consoleDeployGraph');
const {
GITHUB_COMPARE_FILE_LIMIT,
isCompareLikelyTruncated,
isRetryableDownloadStatus,
getDownloadRetryDelayMs,
ensureGoServerSignalRelayPorts,
restoreServerBinaryBackup,
} = require('../services/updateService');
describe('updateService console sync helpers', () => {
test('detects truncated GitHub compare responses', () => {
expect(GITHUB_COMPARE_FILE_LIMIT).toBe(300);
expect(isCompareLikelyTruncated(299)).toBe(false);
expect(isCompareLikelyTruncated(300)).toBe(true);
expect(isCompareLikelyTruncated(450)).toBe(true);
});
test('resolves relative console require paths', () => {
const graph = createConsoleDeployGraph(require('path').join(__dirname, '..'));
expect(graph.resolveConsoleRequire('routes/auth.routes.js', '../services/serverAttestation'))
.toBe('services/serverAttestation.js');
expect(graph.resolveConsoleRequire('routes/index.js', './devices.routes'))
.toBe('routes/devices.routes.js');
expect(graph.resolveConsoleRequire('server.js', './routes'))
.toBe('routes/index.js');
expect(graph.resolveConsoleRequire('server.js', 'express')).toBeNull();
});
test('skips phantom routes.js when routes/index.js exists during repair scan', () => {
const graph = createConsoleDeployGraph(require('path').join(__dirname, '..'));
expect(graph.isResolvedByIndexModule('routes.js')).toBe(true);
expect(graph.isResolvedByIndexModule('routes/auth.routes.js')).toBe(false);
});
test('ignores require examples inside comments when scanning dependencies', () => {
const graph = createConsoleDeployGraph(require('path').join(__dirname, '..'));
const required = graph.collectConsoleRequiredFiles([
{ localPath: 'scripts/linux-ensure-console-user.js' },
]);
expect(required.has('scripts/linux-ensure-console-user.js')).toBe(true);
expect(required.has('scripts/scripts/linux-ensure-console-user.js')).toBe(false);
expect(required.has('routes.js')).toBe(false);
expect(required.has('routes/index.js')).toBe(true);
});
test('collects serverAttestation from auth.routes integrity seeds', () => {
const graph = createConsoleDeployGraph(require('path').join(__dirname, '..'));
const required = graph.collectConsoleRequiredFiles([
{ localPath: 'routes/auth.routes.js' },
]);
expect(required.has('routes/auth.routes.js')).toBe(true);
expect(required.has('services/serverAttestation.js')).toBe(true);
});
test('retries GitHub raw downloads on rate limit status codes', () => {
expect(isRetryableDownloadStatus(429)).toBe(true);
expect(isRetryableDownloadStatus(503)).toBe(true);
expect(isRetryableDownloadStatus(404)).toBe(false);
expect(getDownloadRetryDelayMs(1)).toBe(1000);
expect(getDownloadRetryDelayMs(2)).toBe(2000);
expect(getDownloadRetryDelayMs(6)).toBe(15000);
});
test('ensureGoServerSignalRelayPorts adds SIGNAL_PORT, RELAY_PORT, and GO_API_PORT (#219)', () => {
const unit = [
'[Service]',
'User=root',
'Environment=AUTH_DB_PATH=/opt/console/data/auth.db',
'ExecStart=/opt/betterdesk/betterdesk-server -mode all',
].join('\n');
const patched = ensureGoServerSignalRelayPorts(unit);
expect(patched.changed).toBe(true);
expect(patched.text).toMatch(/^Environment=SIGNAL_PORT=21116$/m);
expect(patched.text).toMatch(/^Environment=RELAY_PORT=21117$/m);
expect(patched.text).toMatch(/^Environment=GO_API_PORT=21114$/m);
const again = ensureGoServerSignalRelayPorts(patched.text);
expect(again.changed).toBe(false);
});
test('restores a validated server binary backup atomically', () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'bd-binary-rollback-'));
const target = path.join(root, 'betterdesk-server.exe');
const backup = `${target}.bak.test`;
try {
fs.writeFileSync(target, 'new');
fs.writeFileSync(backup, 'old');
expect(restoreServerBinaryBackup(backup, target)).toMatchObject({ restored: true });
expect(fs.readFileSync(target, 'utf8')).toBe('old');
} finally {
fs.rmSync(root, { recursive: true, force: true });
}
});
test('rejects a binary backup outside the target directory', () => {
expect(restoreServerBinaryBackup(
path.join(os.tmpdir(), 'betterdesk-server.exe.bak.test'),
path.join(os.tmpdir(), 'other', 'betterdesk-server.exe')
)).toMatchObject({
restored: false,
error: 'Server binary backup path failed validation',
});
});
});