Fix initial artifact deployment and drift file replacement
This commit is contained in:
@@ -53,6 +53,40 @@ async function promotedConfig() {
|
||||
}
|
||||
|
||||
describe('server creation map selection', () => {
|
||||
it('installs only promoted scope-matching scripts and plugins before first boot', async () => {
|
||||
const { createScript } = await import('../../src/lib/server/fleet/scripts.js');
|
||||
const { db } = await import('../../src/lib/server/db/index.js');
|
||||
const { destOfKind } = await import('../../src/lib/server/config.js');
|
||||
const { getServerScript } = await import('../../src/lib/server/fleet/inventory.js');
|
||||
const { createServer } = await import('../../src/lib/server/fleet/create.js');
|
||||
const { komodoDeploy } = await import('../../src/lib/server/integrations/komodo.js');
|
||||
const { scanServerDrift } = await import('../../src/lib/server/fleet/drift.js');
|
||||
const artifacts = [
|
||||
{ kind: 'script' as const, target: 'zm_spawn_fix.gsc', scopeType: 'game' as const, scopeValue: 't5', promoted: true, expected: true },
|
||||
{ kind: 'plugin' as const, target: 'plugin.dll', scopeType: 'map' as const, scopeValue: 'zombie_cosmodrome', promoted: true, expected: true },
|
||||
{ kind: 'script' as const, target: 'other.gsc', scopeType: 'map' as const, scopeValue: 'zombie_theater', promoted: true, expected: false },
|
||||
{ kind: 'script' as const, target: 'draft.gsc', scopeType: 'game' as const, scopeValue: 't5', promoted: false, expected: false }
|
||||
].map((a) => {
|
||||
const { script, version } = createScript({ name: a.target, kind: a.kind, targetRelPath: a.target,
|
||||
scopeType: a.scopeType, scopeValue: a.scopeValue, content: Buffer.from(a.target) });
|
||||
if (a.promoted) db().prepare('UPDATE scripts SET current_version_id = ? WHERE id = ?').run(version.id, script.id);
|
||||
return { ...a, version };
|
||||
});
|
||||
vi.mocked(komodoDeploy).mockImplementationOnce(async () => {
|
||||
for (const a of artifacts) {
|
||||
const path = join(destOfKind(serverName, a.kind), a.target);
|
||||
expect(existsSync(path)).toBe(a.expected);
|
||||
if (a.expected) {
|
||||
expect(await readFile(path)).toEqual(a.version.content);
|
||||
expect(getServerScript(serverName, a.target)).toMatchObject({ script_version_id: a.version.id, source: 'create' });
|
||||
}
|
||||
}
|
||||
return { ok: true };
|
||||
});
|
||||
expect(await createServer(input)).toMatchObject({ ok: true });
|
||||
expect(await scanServerDrift(serverName)).toEqual([]);
|
||||
});
|
||||
|
||||
it('keeps Plutainer defaults without a baseline and overrides the map before rotation', async () => {
|
||||
const { createServer } = await import('../../src/lib/server/fleet/create.js');
|
||||
const { komodoCreateDeployment, komodoDeploy } = await import('../../src/lib/server/integrations/komodo.js');
|
||||
@@ -100,6 +134,11 @@ describe('server creation map selection', () => {
|
||||
|
||||
it('removes the generated config and inventory if deployment fails', async () => {
|
||||
await promotedConfig();
|
||||
const { createScript } = await import('../../src/lib/server/fleet/scripts.js');
|
||||
const { db } = await import('../../src/lib/server/db/index.js');
|
||||
const { script, version } = createScript({ name: 'spawn fix', kind: 'script', scopeType: 'game', scopeValue: 't5',
|
||||
targetRelPath: 'zm_spawn_fix.gsc', content: Buffer.from('main(){}') });
|
||||
db().prepare('UPDATE scripts SET current_version_id = ? WHERE id = ?').run(version.id, script.id);
|
||||
const { createServer } = await import('../../src/lib/server/fleet/create.js');
|
||||
const { komodoDeploy } = await import('../../src/lib/server/integrations/komodo.js');
|
||||
const { getServer } = await import('../../src/lib/server/fleet/servers.js');
|
||||
@@ -108,6 +147,7 @@ describe('server creation map selection', () => {
|
||||
expect(await createServer(input)).toMatchObject({ ok: false, kind: 'komodo' });
|
||||
expect(getServer(serverName)).toBeUndefined();
|
||||
expect(getServerScript(serverName, 'dedicated_sp.cfg')).toBeUndefined();
|
||||
expect(getServerScript(serverName, 'zm_spawn_fix.gsc')).toBeUndefined();
|
||||
expect(existsSync(join(fixture.t5, serverName))).toBe(false);
|
||||
});
|
||||
|
||||
|
||||
@@ -22,6 +22,55 @@ afterEach(async () => {
|
||||
});
|
||||
|
||||
describe('drift scanning', () => {
|
||||
it('replaces a seeded config once instead of planning both removal and installation', async () => {
|
||||
const { seedServerRegistry } = await import('../../src/lib/server/fleet/servers.js');
|
||||
const { createScript } = await import('../../src/lib/server/fleet/scripts.js');
|
||||
const { scanServerDrift, runDriftDeployment } = await import('../../src/lib/server/fleet/drift.js');
|
||||
const { db } = await import('../../src/lib/server/db/index.js');
|
||||
const { getPromotedServerConfig } = await import('../../src/lib/server/fleet/server-config.js');
|
||||
await seedServerRegistry();
|
||||
const server = 't5_zombie_theater_1';
|
||||
const { script, version } = createScript({ name: 'baseline', kind: 'baseline', scopeType: 'game', scopeValue: 't5',
|
||||
content: Buffer.from('set sv_voice "1"\n') });
|
||||
db().prepare('UPDATE scripts SET current_version_id = ? WHERE id = ?').run(version.id, script.id);
|
||||
const path = join(fixture.t5, server, 'configs', 'dedicated_sp.cfg');
|
||||
await writeFile(path, 'seeded config');
|
||||
expect(await scanServerDrift(server)).toEqual([expect.objectContaining({ kind: 'config', driftKind: 'replace' })]);
|
||||
expect(await runDriftDeployment({ servers: [server] })).toMatchObject({ totalRemoved: 0, totalRestored: 1 });
|
||||
expect(await readFile(path)).toEqual(getPromotedServerConfig('t5', 'zombie_theater'));
|
||||
expect(await scanServerDrift(server)).toEqual([]);
|
||||
});
|
||||
|
||||
it.each(['identical', 'different'])('reconciles an existing %s scoped file in one pass with an update audit', async (mode) => {
|
||||
const { seedServerRegistry } = await import('../../src/lib/server/fleet/servers.js');
|
||||
const { createScript } = await import('../../src/lib/server/fleet/scripts.js');
|
||||
const { scanServerDrift, runDriftDeployment } = await import('../../src/lib/server/fleet/drift.js');
|
||||
const { getServerScript } = await import('../../src/lib/server/fleet/inventory.js');
|
||||
const { db } = await import('../../src/lib/server/db/index.js');
|
||||
const { destOfKind } = await import('../../src/lib/server/config.js');
|
||||
await seedServerRegistry();
|
||||
const server = 't5_zombie_theater_1';
|
||||
const content = Buffer.from('main(){}\n');
|
||||
const before = mode === 'identical' ? content : Buffer.from('old bytes');
|
||||
const { script, version } = createScript({ name: 'spawn fix', kind: 'script', scopeType: 'game', scopeValue: 't5',
|
||||
targetRelPath: 'zm_spawn_fix.gsc', content });
|
||||
db().prepare('UPDATE scripts SET current_version_id = ? WHERE id = ?').run(version.id, script.id);
|
||||
const target = join(destOfKind(server, 'script'), 'zm_spawn_fix.gsc');
|
||||
await writeFile(target, before);
|
||||
expect(await scanServerDrift(server)).toEqual([expect.objectContaining({ relPath: 'zm_spawn_fix.gsc', driftKind: 'replace' })]);
|
||||
expect(await runDriftDeployment({ servers: [server], dryRun: true })).toMatchObject({ totalRemoved: 0, totalRestored: 1, deploymentId: null });
|
||||
expect(await readFile(target)).toEqual(before);
|
||||
expect(getServerScript(server, 'zm_spawn_fix.gsc')).toBeUndefined();
|
||||
const result = await runDriftDeployment({ servers: [server] });
|
||||
expect(result).toMatchObject({ totalRemoved: 0, totalRestored: 1 });
|
||||
expect(await readFile(target)).toEqual(content);
|
||||
expect(getServerScript(server, 'zm_spawn_fix.gsc')?.script_version_id).toBe(version.id);
|
||||
expect(db().prepare('SELECT verb, content_before, content_after FROM deployment_files WHERE deployment_id = ?').all(result.deploymentId))
|
||||
.toEqual([{ verb: 'updated', content_before: before, content_after: content }]);
|
||||
expect(await scanServerDrift(server)).toEqual([]);
|
||||
expect((await runDriftDeployment({ servers: [server] })).deploymentId).toBeNull();
|
||||
});
|
||||
|
||||
it('detects untracked files across script, config, and plugin destinations', async () => {
|
||||
const { seedServerRegistry } = await import('../../src/lib/server/fleet/servers.js');
|
||||
const { scanServerDrift } = await import('../../src/lib/server/fleet/drift.js');
|
||||
|
||||
Reference in New Issue
Block a user