Add House Party Protocol for fleet-wide queued destruction

This commit is contained in:
2026-09-18 00:39:19 -04:00
parent 586133c711
commit 274aedb66e
7 changed files with 212 additions and 3 deletions
+62
View File
@@ -41,6 +41,68 @@ afterEach(async () => {
vi.unstubAllEnvs();
});
describe('House Party Protocol', () => {
it('includes registered and queued servers once and reuses destruction requests', async () => {
const { createServer, queueCreateServer } = await import('../../src/lib/server/fleet/create.js');
const { housePartyTargets, queueHouseParty } = await import('../../src/lib/server/fleet/house-party.js');
expect(await createServer(input)).toMatchObject({ ok: true });
expect(queueCreateServer(input)).toMatchObject({ ok: true });
const names = housePartyTargets();
expect(names).toEqual([serverName, 't5_zombie_cosmodrome_2']);
const first = queueHouseParty(names);
expect(first).toMatchObject({ ok: true, deployments: [{ server: serverName }, { server: names[1] }] });
expect(queueHouseParty(names)).toEqual(first);
});
it('rejects a changed fleet without queuing any destruction', async () => {
const { queueCreateServer } = await import('../../src/lib/server/fleet/create.js');
const { housePartyTargets, queueHouseParty } = await import('../../src/lib/server/fleet/house-party.js');
const { db } = await import('../../src/lib/server/db/index.js');
const names = housePartyTargets();
expect(queueCreateServer(input).ok).toBe(true);
expect(queueHouseParty(names).ok).toBe(false);
expect(db().prepare("SELECT * FROM server_deployments WHERE action = 'destroy'").all()).toEqual([]);
});
it('rolls back the entire bulk request when an insert fails', async () => {
const { queueCreateServer } = await import('../../src/lib/server/fleet/create.js');
const { housePartyTargets, queueHouseParty } = await import('../../src/lib/server/fleet/house-party.js');
const { db } = await import('../../src/lib/server/db/index.js');
expect(queueCreateServer(input).ok).toBe(true);
expect(queueCreateServer(input).ok).toBe(true);
db().exec(`CREATE TRIGGER reject_destroy BEFORE INSERT ON server_deployments
WHEN NEW.action = 'destroy' AND NEW.server = 't5_zombie_cosmodrome_2'
BEGIN SELECT RAISE(ABORT, 'test failure'); END`);
expect(() => queueHouseParty(housePartyTargets())).toThrow('test failure');
expect(db().prepare("SELECT * FROM server_deployments WHERE action = 'destroy'").all()).toEqual([]);
});
it('waits for queued creations, then for verified emptiness, and preserves keys', async () => {
const { queueCreateServer, processCreations, processDestructions } = await import('../../src/lib/server/fleet/create.js');
const { housePartyTargets, queueHouseParty } = await import('../../src/lib/server/fleet/house-party.js');
const { getServer } = await import('../../src/lib/server/fleet/servers.js');
const { getServerKey } = await import('../../src/lib/server/fleet/server-keys.js');
const { komodoDestroyDeployment, komodoInspectHealthy } = await import('../../src/lib/server/integrations/komodo.js');
const { rconGetPlayerCount } = await import('../../src/lib/server/integrations/rcon.js');
expect(queueCreateServer(input).ok).toBe(true);
expect(queueHouseParty(housePartyTargets()).ok).toBe(true);
await processDestructions();
expect(komodoDestroyDeployment).not.toHaveBeenCalled();
await processCreations();
vi.mocked(rconGetPlayerCount).mockResolvedValueOnce(2);
await processDestructions();
vi.mocked(rconGetPlayerCount).mockResolvedValueOnce(null);
await processDestructions();
expect(komodoDestroyDeployment).not.toHaveBeenCalled();
vi.mocked(rconGetPlayerCount).mockResolvedValueOnce(0);
vi.mocked(komodoInspectHealthy).mockResolvedValue({ ok: false, kind: 'not-found', message: 'gone' } as never);
await processDestructions();
expect(komodoDestroyDeployment).toHaveBeenCalledWith(serverName);
expect(getServer(serverName)).toBeUndefined();
expect(getServerKey(serverName)).toBe('test-key');
});
});
async function promotedConfig() {
const { createScript, getLatestVersion } = await import('../../src/lib/server/fleet/scripts.js');
const { db } = await import('../../src/lib/server/db/index.js');