Fix native health polling and isolate file operation traffic

This commit is contained in:
2026-10-04 21:50:29 -04:00
parent 8a31de92fa
commit 81f088739c
22 changed files with 446 additions and 44 deletions
+24
View File
@@ -5,6 +5,7 @@ import { komodoConfigured, rconConfigured } from '../../src/lib/server/config.js
import { getServer, type ServerRow } from '../../src/lib/server/fleet/servers.js';
import { latestGameSubmission } from '../../src/lib/server/fleet/lifecycle-evidence.js';
import { rconGetPlayerCount, rconPlutoniumVersion, rconStatusResult } from '../../src/lib/server/integrations/rcon.js';
import { nativeInstance, nativeRequest } from '../../src/lib/server/backends/native.js';
vi.mock('node:dgram', () => ({ createSocket: vi.fn() }));
vi.mock('../../src/lib/server/config.js', () => ({
@@ -14,6 +15,7 @@ vi.mock('../../src/lib/server/config.js', () => ({
vi.mock('../../src/lib/server/fleet/servers.js', () => ({ getServer: vi.fn(), setServerField: vi.fn() }));
vi.mock('../../src/lib/server/fleet/lifecycle-evidence.js', () => ({ latestGameSubmission: vi.fn() }));
vi.mock('../../src/lib/server/integrations/komodo.js', () => ({ komodoResolveGamePort: vi.fn() }));
vi.mock('../../src/lib/server/backends/native.js', () => ({ nativeInstance: vi.fn(), nativeRequest: vi.fn() }));
class FakeSocket extends EventEmitter {
send = vi.fn();
@@ -47,6 +49,28 @@ afterEach(() => {
});
describe('RCON status transport', () => {
it.each([
['timeout', 'timeout', 'Agent received no reply'],
['unreachable', 'send-error', 'Agent could not reach'],
['unconfigured', 'no-config', 'Agent RCON password'],
['authentication', 'bad-reply', 'wrong RCON password'],
['invalid-reply', 'bad-reply', 'Agent RCON reply is missing']
])('preserves the native agent %s diagnosis', async (agentReason, reason, detail) => {
server.native_instance_id = 'instance';
vi.mocked(nativeInstance).mockReturnValue({ id: 'instance', generation: 1, host_id: 'host', port: 28960 } as never);
vi.mocked(nativeRequest).mockResolvedValue({ ok: false, reason: agentReason, text: '' });
expect(await rconStatusResult(server.name)).toMatchObject({ ok: false, reason, detail: expect.stringContaining(detail) });
expect(nativeRequest).toHaveBeenCalledOnce();
expect(sockets).toHaveLength(0);
});
it('uses the successful native reply after agent-side recovery', async () => {
server.native_instance_id = 'instance';
vi.mocked(nativeInstance).mockReturnValue({ id: 'instance', generation: 1, host_id: 'host', port: 28960 } as never);
vi.mocked(nativeRequest).mockResolvedValue({ ok: true, text: 'map: mp_kino\nnum score ping guid name\n--- ----- ---- ---- ----\n' });
expect(await rconStatusResult(server.name)).toMatchObject({ ok: true, status: { players: 0, map: 'mp_kino' } });
});
it('shares overlapping health and occupancy probes and probes again after completion', async () => {
const health = rconStatusResult(server.name);
const occupancy = rconGetPlayerCount(server.name);