mirror of
https://github.com/UNITRONIX/BetterDesk.git
synced 2026-10-25 22:10:49 +00:00
d57fe3dc49
Adds local verification and distribution of signed generic incoming-only agent releases, mobile-friendly QR activation, and the supporting generator interfaces. Includes the current staged console, documentation, localization, and builder changes.\n\nThanks: INSOLVE (Honorary); Marco Jakobs (@jacotec); MyNameisStitch (@MyNameisStitch); Redspin (@playerumpknow)
155 lines
6.8 KiB
JavaScript
155 lines
6.8 KiB
JavaScript
'use strict';
|
|
|
|
const fs = require('fs');
|
|
const os = require('os');
|
|
const path = require('path');
|
|
|
|
describe('clientBuilderService', () => {
|
|
let dataDir;
|
|
let builder;
|
|
|
|
beforeEach(() => {
|
|
jest.resetModules();
|
|
dataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'betterdesk-client-builder-'));
|
|
jest.doMock('../config/config', () => ({ dataDir }));
|
|
builder = require('../services/clientBuilderService');
|
|
});
|
|
|
|
afterEach(() => {
|
|
jest.dontMock('../config/config');
|
|
fs.rmSync(dataDir, { recursive: true, force: true });
|
|
});
|
|
|
|
test('creates stable, distinct generation keys', () => {
|
|
const fingerprint = 'a'.repeat(64);
|
|
const first = builder.generationKey(fingerprint, '1'.repeat(40));
|
|
const same = builder.generationKey(fingerprint, '1'.repeat(40));
|
|
const changed = builder.generationKey(fingerprint, '2'.repeat(40));
|
|
|
|
expect(first).toMatch(/^[a-f0-9]{64}$/);
|
|
expect(same).toBe(first);
|
|
expect(changed).not.toBe(first);
|
|
});
|
|
|
|
test('rejects unsafe repository and ref values', () => {
|
|
expect(() => builder.normalizeRepo('https://github.com/example/repo')).toThrow(
|
|
'betterdesk_client_repo_invalid'
|
|
);
|
|
expect(() => builder.normalizeRepo('example/repo/extra')).toThrow(
|
|
'betterdesk_client_repo_invalid'
|
|
);
|
|
expect(() => builder.clientRef('master\r\nTOKEN=leak')).toThrow(
|
|
'betterdesk_client_ref_invalid'
|
|
);
|
|
});
|
|
|
|
test('writes state atomically outside the project checkout', async () => {
|
|
const state = await builder.writeState({
|
|
repo: 'UNITRONIX/BetterDesk-Client',
|
|
ref: 'master',
|
|
installedCommit: 'a'.repeat(40),
|
|
status: 'ready',
|
|
});
|
|
|
|
expect(state.installedCommit).toBe('a'.repeat(40));
|
|
expect(fs.existsSync(builder.statePath())).toBe(true);
|
|
expect(builder.statePath()).toContain(path.join('modules', 'betterdesk-client-builder'));
|
|
expect(fs.readdirSync(builder.dataRoot()).some((name) => name.endsWith('.tmp'))).toBe(false);
|
|
});
|
|
|
|
test('generates an instance signing seed without exposing it in state', () => {
|
|
const key = builder.ensureSigningKeySync();
|
|
const seedPath = path.join(
|
|
dataDir,
|
|
'modules',
|
|
'betterdesk-support-generator',
|
|
'custom-client-signing.seed'
|
|
);
|
|
expect(key.generated).toBe(true);
|
|
expect(key.publicKey).toMatch(/^[A-Za-z0-9+/]+=*$/);
|
|
expect(fs.readFileSync(seedPath, 'utf8').trim()).toMatch(/^[A-Za-z0-9+/]+=*$/);
|
|
expect(JSON.stringify(builder.readState())).not.toContain(key.seed);
|
|
});
|
|
|
|
test('loads the installer environment without overriding explicit process values', () => {
|
|
const envDir = fs.mkdtempSync(path.join(os.tmpdir(), 'betterdesk-build-env-'));
|
|
const envFile = path.join(envDir, 'build.env');
|
|
const previousFile = process.env.BETTERDESK_BUILD_ENV_FILE;
|
|
const previousCargo = process.env.BETTERDESK_CLIENT_CARGO;
|
|
const previousVcpkg = process.env.VCPKG_ROOT;
|
|
try {
|
|
fs.writeFileSync(envFile, [
|
|
'BETTERDESK_CLIENT_CARGO=/opt/cargo/bin/cargo',
|
|
'VCPKG_ROOT=/opt/vcpkg',
|
|
].join('\n'));
|
|
process.env.BETTERDESK_BUILD_ENV_FILE = envFile;
|
|
process.env.BETTERDESK_CLIENT_CARGO = '/explicit/cargo';
|
|
delete process.env.VCPKG_ROOT;
|
|
jest.resetModules();
|
|
const reloaded = require('../services/clientBuilderService');
|
|
expect(process.env.BETTERDESK_CLIENT_CARGO).toBe('/explicit/cargo');
|
|
expect(process.env.VCPKG_ROOT).toBe('/opt/vcpkg');
|
|
expect(reloaded.getBuildCapabilities).toBeDefined();
|
|
} finally {
|
|
if (previousFile === undefined) delete process.env.BETTERDESK_BUILD_ENV_FILE;
|
|
else process.env.BETTERDESK_BUILD_ENV_FILE = previousFile;
|
|
if (previousCargo === undefined) delete process.env.BETTERDESK_CLIENT_CARGO;
|
|
else process.env.BETTERDESK_CLIENT_CARGO = previousCargo;
|
|
if (previousVcpkg === undefined) delete process.env.VCPKG_ROOT;
|
|
else process.env.VCPKG_ROOT = previousVcpkg;
|
|
fs.rmSync(envDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test('reports only explicitly configured targets for a remote provider', () => {
|
|
const previousCommand = process.env.BETTERDESK_CLIENT_BUILD_COMMAND;
|
|
const previousTargets = process.env.BETTERDESK_CLIENT_PROVIDER_TARGETS_JSON;
|
|
try {
|
|
process.env.BETTERDESK_CLIENT_BUILD_COMMAND = 'trusted-remote-provider';
|
|
process.env.BETTERDESK_CLIENT_PROVIDER_TARGETS_JSON = JSON.stringify({
|
|
targets: [
|
|
{ platform: 'windows', arch: 'x64', format: 'portable' },
|
|
{ platform: 'linux', arch: 'arm64', format: 'deb' },
|
|
{ platform: 'not-supported', arch: 'x64', format: 'portable' },
|
|
],
|
|
});
|
|
|
|
expect(builder.getBuildCapabilities()).toMatchObject({
|
|
mode: 'remote',
|
|
provider: 'command',
|
|
buildReady: true,
|
|
targets: [
|
|
{ platform: 'windows', arch: 'x64', format: 'portable' },
|
|
{ platform: 'linux', arch: 'arm64', format: 'deb' },
|
|
],
|
|
});
|
|
} finally {
|
|
if (previousCommand === undefined) delete process.env.BETTERDESK_CLIENT_BUILD_COMMAND;
|
|
else process.env.BETTERDESK_CLIENT_BUILD_COMMAND = previousCommand;
|
|
if (previousTargets === undefined) delete process.env.BETTERDESK_CLIENT_PROVIDER_TARGETS_JSON;
|
|
else process.env.BETTERDESK_CLIENT_PROVIDER_TARGETS_JSON = previousTargets;
|
|
}
|
|
});
|
|
|
|
test('does not advertise a remote provider without target capabilities', () => {
|
|
const previousCommand = process.env.BETTERDESK_CLIENT_BUILD_COMMAND;
|
|
const previousTargets = process.env.BETTERDESK_CLIENT_PROVIDER_TARGETS_JSON;
|
|
try {
|
|
process.env.BETTERDESK_CLIENT_BUILD_COMMAND = 'trusted-remote-provider';
|
|
delete process.env.BETTERDESK_CLIENT_PROVIDER_TARGETS_JSON;
|
|
|
|
expect(builder.getBuildCapabilities()).toMatchObject({
|
|
mode: 'remote',
|
|
buildReady: false,
|
|
targets: [],
|
|
reason: 'provider_targets_missing',
|
|
});
|
|
} finally {
|
|
if (previousCommand === undefined) delete process.env.BETTERDESK_CLIENT_BUILD_COMMAND;
|
|
else process.env.BETTERDESK_CLIENT_BUILD_COMMAND = previousCommand;
|
|
if (previousTargets === undefined) delete process.env.BETTERDESK_CLIENT_PROVIDER_TARGETS_JSON;
|
|
else process.env.BETTERDESK_CLIENT_PROVIDER_TARGETS_JSON = previousTargets;
|
|
}
|
|
});
|
|
});
|