Files
BetterDesk/web-nodejs/tests/bootstrapAdminCredentials.test.js
UNITRONIX 03efbde268 fix(docker): restore Web Console CI bootstrap/guard gates
Make guard-sqlite-auth-split self-contained via _guard_file_exists so
orphan auth.db detection works when the script is sourced alone (unit
tests) while still preferring file_exists_as_betterdesk under entrypoints.
Align bootstrap credential tests with #385 (generate/export shared
password; publish ADMIN_PASSWORD to .admin_credentials).

Thanks: INSOLVE (Honorary); Marco Jakobs (@jacotec); MyNameisStitch (@MyNameisStitch); Redspin (@playerumpknow); muopc (@muopc)
2026-10-17 16:03:35 +02:00

75 lines
2.8 KiB
JavaScript

'use strict';
/**
* Docker bootstrap-admin-credentials.sh — idempotent shared password (issue #385).
* Runs only when sh is available (Linux/macOS CI and Docker build hosts).
*/
const { spawnSync } = require('child_process');
const fs = require('fs');
const os = require('os');
const path = require('path');
const SCRIPT = path.join(__dirname, '..', '..', 'docker', 'bootstrap-admin-credentials.sh');
// Match dockerBootstrapCredentials.test.js: run whenever a POSIX sh is on PATH
// (Linux CI, Git Bash on Windows). Skip only when sh is unavailable.
const shAvailable = spawnSync('sh', ['-c', 'exit 0'], { stdio: 'ignore' }).status === 0;
(shAvailable ? describe : describe.skip)('bootstrap-admin-credentials.sh', () => {
let tmpDir;
beforeEach(() => {
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'bd-bootstrap-'));
});
afterEach(() => {
fs.rmSync(tmpDir, { recursive: true, force: true });
});
function runBootstrap(extraEnv = {}) {
return spawnSync('sh', [SCRIPT], {
env: {
...process.env,
RUSTDESK_PATH: tmpDir,
DATA_DIR: path.join(tmpDir, 'data'),
...extraEnv,
},
encoding: 'utf8',
});
}
test('generates credentials file and exports matching env vars', () => {
const result = runBootstrap();
expect(result.status).toBe(0);
const credsFile = path.join(tmpDir, '.admin_credentials');
expect(fs.existsSync(credsFile)).toBe(true);
const content = fs.readFileSync(credsFile, 'utf8');
const match = content.match(/^Admin Password:\s*(.+)$/m);
expect(match).not.toBeNull();
expect(match[1].trim().length).toBeGreaterThan(0);
expect(result.stdout).toContain('Bootstrap admin credentials');
});
test('does not overwrite existing credentials file', () => {
const credsFile = path.join(tmpDir, '.admin_credentials');
fs.mkdirSync(tmpDir, { recursive: true });
fs.writeFileSync(credsFile, 'Admin Username: admin\nAdmin Password: existing-secret\n', { mode: 0o600 });
const first = runBootstrap();
expect(first.status).toBe(0);
expect(fs.readFileSync(credsFile, 'utf8')).toContain('existing-secret');
});
test('publishes ADMIN_PASSWORD to .admin_credentials on a fresh volume', () => {
// Operators and show-admin-credentials must read the same password as
// INIT_ADMIN_PASS / DEFAULT_ADMIN_PASSWORD (issue #385).
const result = runBootstrap({ ADMIN_PASSWORD: 'preset-password-12345' });
expect(result.status).toBe(0);
const credsFile = path.join(tmpDir, '.admin_credentials');
expect(fs.existsSync(credsFile)).toBe(true);
expect(fs.readFileSync(credsFile, 'utf8')).toContain('Admin Password: preset-password-12345');
});
});