Remove Komodo backend in favor of native hosts

This commit is contained in:
2026-10-05 18:07:07 -04:00
parent 701066aaf8
commit 4b0f341d64
116 changed files with 557 additions and 8132 deletions
+21 -9
View File
@@ -6,9 +6,26 @@ import { newFixture, type Fixture } from '../helpers/fixture.js';
import { gameEvent, postTelemetry } from '../helpers/telemetry.js';
import { defaultPlan, requirement, type BetaPlan } from '../../src/lib/beta-plan.js';
vi.mock('../../src/lib/server/integrations/komodo.js', () => ({ komodoRestart: vi.fn(), komodoInspectHealthy: vi.fn() }));
vi.mock('../../src/lib/server/integrations/rcon.js', () => ({ rconGetPlayerCount: vi.fn(), rconStatusResult: vi.fn() }));
vi.mock('../../src/lib/server/pipeline/worker.js', () => ({ kick: vi.fn() }));
// Planner tests observe the provisioning contract. Host command delivery and
// disruption safety are exercised by the native provisioning suites.
vi.mock('../../src/lib/server/provisioning/worker.js', () => ({
processProvisioning: async () => {
const { db } = await import('../../src/lib/server/db/index.js');
const { applyStagedFilesForOperation } = await import('../../src/lib/server/fleet/staged-files.js');
const { probePlayerEvidence } = await import('../../src/lib/server/fleet/status-evidence.js');
const jobs = db().prepare("SELECT id,server,state FROM server_deployments WHERE action='restart' AND state IN ('waiting','awaiting-health')").all() as { id: string; server: string; state: string }[];
for (const job of jobs) {
if (job.state === 'awaiting-health') {
db().prepare("UPDATE server_deployments SET state='done',finished_at=? WHERE id=?").run(new Date().toISOString(), job.id);
} else if ((await probePlayerEvidence(job.server)).players === 0) {
applyStagedFilesForOperation(job.id, job.server);
db().prepare("UPDATE server_deployments SET state='awaiting-health',submitted_at=? WHERE id=?").run(new Date().toISOString(), job.id);
}
}
}
}));
let fixture: Fixture;
const names = ['t5_zombie_theater_1', 't5_zombie_theater_2'];
const now = () => new Date().toISOString();
@@ -21,14 +38,11 @@ beforeEach(async () => {
await writeFile(join(fixture.t5, name, 'logs/console.log'), 'old script error\n');
await writeFile(join(fixture.t5, name, 'logs/games_sp.log'), 'InitGame:\n 0:00 J;old;1;old\nExitLevel: executed\n');
}
vi.stubEnv('KOMODO_ADDRESS', 'http://komodo.test'); vi.stubEnv('KOMODO_API_KEY', 'test');
vi.stubEnv('KOMODO_API_SECRET', 'test'); vi.stubEnv('RCON_PASSWORD', 'test'); vi.stubEnv('DEPLOYMENT_RESTART_CONCURRENCY', '2');
vi.stubEnv('RCON_PASSWORD', 'test'); vi.stubEnv('DEPLOYMENT_RESTART_CONCURRENCY', '2');
const { seedServerRegistry } = await import('../../src/lib/server/fleet/servers.js');
await seedServerRegistry();
const { komodoRestart, komodoInspectHealthy } = await import('../../src/lib/server/integrations/komodo.js');
const { rconGetPlayerCount, rconStatusResult } = await import('../../src/lib/server/integrations/rcon.js');
vi.mocked(komodoRestart).mockResolvedValue({ ok: true });
vi.mocked(komodoInspectHealthy).mockResolvedValue({ ok: true, state: 'running' });
vi.mocked(rconGetPlayerCount).mockResolvedValue(0);
vi.mocked(rconStatusResult).mockResolvedValue({ ok: true, endpoint: { host: 'test', port: 1 }, rawReply: '', status: { players: 0, map: 'zombie_theater', gametype: 'zom', roster: [] } });
});
@@ -57,14 +71,12 @@ describe('durable beta test plans', () => {
it('waits for occupied telemetry to clear before restarting a beta despite an empty RCON response', async () => {
const id = await openPlan(defaultPlan());
const { listBetaServers } = await import('../../src/lib/server/fleet/beta.js');
const { komodoRestart } = await import('../../src/lib/server/integrations/komodo.js');
await postTelemetry(gameEvent('heartbeat', 4, 2));
await tick();
expect(komodoRestart).not.toHaveBeenCalled();
expect(listBetaServers(id)[0].stage).toBe('awaiting-empty');
await postTelemetry(gameEvent('heartbeat', 4, 0));
await tick();
expect(komodoRestart).toHaveBeenCalledTimes(1);
expect(listBetaServers(id)[0].stage).toBe('awaiting-health');
});
it('holds file deployment until each wave passes, and rollback leaves undeployed files alone', async () => {