From 5428b5a2dbac7924af37396ed1f191e8dade12b7 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Mon, 18 May 2026 14:53:36 +0200 Subject: [PATCH] T-124 follow-up: cover server.dart error paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Net new uncovered lines in lib/src/ipc/server.dart from the T-124 commit (handler-throw → toolError, non-request → userError, in-flight client teardown on stop, multi-request on one connection) are covered here so the coverage gate holds at 95%. Co-Authored-By: Claude --- test/ipc/server_test.dart | 65 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/test/ipc/server_test.dart b/test/ipc/server_test.dart index 83e786b3..9c94b132 100644 --- a/test/ipc/server_test.dart +++ b/test/ipc/server_test.dart @@ -161,6 +161,71 @@ void main() { await server.stop(); expect(server.isRunning, isFalse); }); + + test('a handler that throws surfaces as a toolError response', () async { + dispatcher.register('boom', (_) async => throw StateError('handler crash')); + server = IpcServer(dispatcher: dispatcher, workspaceRoot: workRoot, log: _silentLog()); + await server.start(); + final reply = await _roundTrip(server.socketPath, IpcRequest(id: 'x', cmd: 'boom')); + expect(reply.ok, isFalse); + expect(reply.error?.kind, IpcErrorKind.toolError); + expect(reply.error?.message, contains('handler crash')); + }); + + test('stop closes an in-flight client connection', () async { + server = IpcServer(dispatcher: dispatcher, workspaceRoot: workRoot, log: _silentLog()); + await server.start(); + final c = await Socket.connect( + InternetAddress(server.socketPath, type: InternetAddressType.unix), + 0, + ); + c.write('${IpcRequest(id: 'q', cmd: 'ping').encode()}\n'); + await c.flush(); + await c.cast>().transform(utf8.decoder).transform(const LineSplitter()).first; + await server.stop(); + expect(server.isRunning, isFalse); + try { + await c.close(); + } catch (_) {} + }); + + test('multiple sequential requests on the same connection each get a reply', () async { + server = IpcServer(dispatcher: dispatcher, workspaceRoot: workRoot, log: _silentLog()); + await server.start(); + final c = await Socket.connect( + InternetAddress(server.socketPath, type: InternetAddressType.unix), + 0, + ); + final replies = c.cast>().transform(utf8.decoder).transform(const LineSplitter()); + final iter = StreamIterator(replies); + for (var i = 0; i < 3; i++) { + c.write('${IpcRequest(id: '$i', cmd: 'ping').encode()}\n'); + await c.flush(); + expect(await iter.moveNext().timeout(const Duration(seconds: 2)), isTrue); + final reply = IpcMessage.decode(iter.current) as IpcResponse; + expect(reply.id, '$i'); + expect(reply.ok, isTrue); + } + await iter.cancel(); + await c.close(); + }); + + test('a non-request message (e.g. event) surfaces a userError', () async { + server = IpcServer(dispatcher: dispatcher, workspaceRoot: workRoot, log: _silentLog()); + await server.start(); + final c = await Socket.connect( + InternetAddress(server.socketPath, type: InternetAddressType.unix), + 0, + ); + final evt = IpcEvent(subsystem: 'test', kind: 'wrong-shape', timestamp: DateTime.now().toUtc()); + c.write('${evt.encode()}\n'); + await c.flush(); + final line = await c.cast>().transform(utf8.decoder).transform(const LineSplitter()).first.timeout(const Duration(seconds: 2)); + await c.close(); + final reply = IpcMessage.decode(line) as IpcResponse; + expect(reply.ok, isFalse); + expect(reply.error?.kind, IpcErrorKind.userError); + }); }); }