Preserve runtime passwords when revalidating launch files

This commit is contained in:
2026-10-08 11:31:26 -04:00
parent f8942c732a
commit 8863e8a882
4 changed files with 76 additions and 10 deletions
+34 -5
View File
@@ -179,17 +179,23 @@ describe('asynchronous native launch preparation', () => {
expect(db().prepare('SELECT state FROM server_deployments WHERE id=?').get(job.deploymentId)).toEqual({ state: 'cancelled' });
});
it('discards prepared bytes when the promoted version changes before dispatch', async () => {
it.each(['baseline', 'config', 'script', 'plugin'] as const)('discards prepared bytes when the promoted %s changes before dispatch', async kind => {
const { createScript, uploadNewVersion } = await import('../../src/lib/server/fleet/scripts.js');
const { db } = await import('../../src/lib/server/db/index.js');
const { script, version } = createScript({ name: 'Launch plugin', kind: 'plugin', targetRelPath: 'fixture.dll',
scopeType: 'game', scopeValue: 't5', content: Buffer.from('before promotion') });
const baseline = createScript({ name: 'Launch baseline', kind: 'baseline', scopeType: 'game', scopeValue: 't5',
content: Buffer.from('set rcon_password "password"\nset sv_voice "1"\n') });
db().prepare('UPDATE scripts SET current_version_id=? WHERE id=?').run(baseline.version.id, baseline.script.id);
const isConfig = kind === 'baseline' || kind === 'config';
const { script, version } = kind === 'baseline' ? baseline : createScript({ name: `Launch ${kind}`, kind,
targetRelPath: kind === 'script' ? 'fixture.gsc' : kind === 'plugin' ? 'fixture.dll' : undefined,
scopeType: kind === 'config' ? 'map' : 'game', scopeValue: kind === 'config' ? 'zombie_theater' : 't5',
content: Buffer.from(isConfig ? 'set sv_voice "1"\n' : 'before promotion') });
db().prepare('UPDATE scripts SET current_version_id=? WHERE id=?').run(version.id, script.id);
const job = await queue();
const paused = await pausePreparation();
const pass = runPass();
await paused.ready;
const content = Buffer.from('newly promoted plugin');
const content = Buffer.from(isConfig ? 'set rcon_password "password"\nset sv_voice "0"\n' : 'newly promoted file');
const latest = uploadNewVersion(script.id, content);
db().prepare('UPDATE scripts SET current_version_id=? WHERE id=?').run(latest.id, script.id);
paused.release();
@@ -200,7 +206,30 @@ describe('asynchronous native launch preparation', () => {
expect(operation).toBeDefined();
const { getHostOperation } = await import('../../src/lib/server/hosts/index.js');
const payload = getHostOperation(operation!.id)!.payload as { files: { contentBase64: string; sha256: string }[] };
expect(payload.files).toContainEqual(expect.objectContaining({ contentBase64: content.toString('base64'), sha256: latest.sha256 }));
if (isConfig) {
const { getPromotedServerConfig } = await import('../../src/lib/server/fleet/server-config.js');
const expected = getPromotedServerConfig('t5', 'zombie_theater', 'theater', 'test-password')!;
expect(payload.files).toContainEqual(expect.objectContaining({ contentBase64: expected.toString('base64') }));
expect(expected.toString()).toContain('set sv_voice "0"');
} else expect(payload.files).toContainEqual(expect.objectContaining({ contentBase64: content.toString('base64'), sha256: latest.sha256 }));
});
it('discards prepared bytes when the server key changes before dispatch', async () => {
const job = await queue();
const paused = await pausePreparation();
const pass = runPass();
await paused.ready;
const { db } = await import('../../src/lib/server/db/index.js');
db().prepare(`UPDATE server_keys SET pluto_key='changed-key' WHERE id IN
(SELECT key_id FROM server_key_leases WHERE server_id=? AND is_current=1)`).run(job.name);
paused.release();
await pass;
expect(await operationFor(job.deploymentId)).toBeUndefined();
await runPass();
const operation = await operationFor(job.deploymentId);
expect(operation).toBeDefined();
const { getHostOperation } = await import('../../src/lib/server/hosts/index.js');
expect(getHostOperation(operation!.id)!.payload).toMatchObject({ env: { PLUTO_SERVER_KEY: 'changed-key' } });
});
it('prepares only one launch while the single startup slot is reserved', async () => {