/** * Integration test: drive the wire format with raw sockets to exercise * the same code paths that KRPCClient uses internally. * * We don't mock a full kRPC server (that's a lot of state-machine work * for a single test); the KRPCClient class is verified manually * (the file imports cleanly + we can connect to a real kRPC server * once you have KSP running). */ import { describe, it, expect } from 'vitest'; import * as net from 'node:net'; import { Buffer } from 'node:buffer'; import { KRPC, encodeMessage, decodeMessage } from '../src/schema.js'; import { sendMessage, recvMessage, encodeVarint } from '../src/connection.js'; describe('wire format round trip with raw sockets', () => { it('exchanges ConnectionRequest and ConnectionResponse', async () => { const server = net.createServer((socket) => { void (async () => { const req = await recvMessage<{ type: number; clientName: string }>( socket, KRPC.ConnectionRequest, ); expect(req.type).toBe(0); // RPC expect(req.clientName).toBe('test'); const resp = encodeMessage(KRPC.ConnectionResponse, { status: 0, message: '', clientIdentifier: Buffer.from('test-client-id'), }); socket.write(Buffer.concat([encodeVarint(resp.length), resp])); await new Promise((r) => setTimeout(r, 50)); socket.destroy(); })(); }); await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)); const port = (server.address() as net.AddressInfo).port; const client = net.createConnection({ host: '127.0.0.1', port }); await new Promise((resolve) => client.once('connect', () => resolve())); // Handshake sendMessage(client, KRPC.ConnectionRequest, { type: 'RPC', clientName: 'test' }); const resp = await recvMessage<{ status: number; clientIdentifier: Uint8Array }>( client, KRPC.ConnectionResponse, ); expect(resp.status).toBe(0); expect(Buffer.from(resp.clientIdentifier).toString()).toBe('test-client-id'); client.destroy(); await new Promise((resolve) => server.close(() => resolve())); }); it('exchanges a Request with a Response carrying a Status return value', async () => { const server = net.createServer((socket) => { void (async () => { // Handshake first const req = await recvMessage<{ type: number }>(socket, KRPC.ConnectionRequest); expect(req.type).toBe(0); const resp = encodeMessage(KRPC.ConnectionResponse, { status: 0, message: '', clientIdentifier: Buffer.from('x'), }); socket.write(Buffer.concat([encodeVarint(resp.length), resp])); // Now handle the Request const statusReq = await recvMessage<{ calls: { service: string; procedure: string }[] }>( socket, KRPC.Request, ); expect(statusReq.calls.length).toBe(1); expect(statusReq.calls[0]!.service).toBe('KRPC'); expect(statusReq.calls[0]!.procedure).toBe('GetStatus'); const status = encodeMessage(KRPC.Status, { version: '0.5.0', bytesRead: 100n, bytesWritten: 50n, bytesReadRate: 1.0, bytesWrittenRate: 0.5, }); const respMsg = encodeMessage(KRPC.Response, { results: [{ value: status }] }); socket.write(Buffer.concat([encodeVarint(respMsg.length), respMsg])); await new Promise((r) => setTimeout(r, 50)); socket.destroy(); })(); }); await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)); const port = (server.address() as net.AddressInfo).port; const client = net.createConnection({ host: '127.0.0.1', port }); await new Promise((resolve) => client.once('connect', () => resolve())); sendMessage(client, KRPC.ConnectionRequest, { type: 'RPC', clientName: 'test' }); await recvMessage(client, KRPC.ConnectionResponse); sendMessage(client, KRPC.Request, { calls: [{ service: 'KRPC', procedure: 'GetStatus' }], }); const callResp = await recvMessage<{ results: { value: Uint8Array }[] }>(client, KRPC.Response); const status = decodeMessage<{ version: string }>(KRPC.Status, callResp.results[0]!.value); expect(status.version).toBe('0.5.0'); client.destroy(); await new Promise((resolve) => server.close(() => resolve())); }); });