|
32 | 32 |
|
33 | 33 | import com.agentclientprotocol.sdk.agent.AcpAgent; |
34 | 34 | import com.agentclientprotocol.sdk.agent.AcpAgentFactory; |
| 35 | +import com.agentclientprotocol.sdk.agent.AcpAsyncAgent; |
| 36 | +import com.agentclientprotocol.sdk.capabilities.NegotiatedCapabilities; |
35 | 37 | import com.agentclientprotocol.sdk.client.AcpAsyncClient; |
36 | 38 | import com.agentclientprotocol.sdk.client.AcpClient; |
37 | 39 | import com.agentclientprotocol.sdk.client.transport.WebSocketAcpClientTransport; |
|
41 | 43 | import org.junit.jupiter.api.Test; |
42 | 44 | import reactor.core.publisher.Flux; |
43 | 45 | import reactor.core.publisher.Mono; |
| 46 | +import reactor.core.publisher.Sinks; |
44 | 47 | import reactor.core.scheduler.Schedulers; |
45 | 48 |
|
46 | 49 | import static org.assertj.core.api.Assertions.assertThat; |
@@ -325,6 +328,30 @@ void rejectsDuplicateInitializeWithoutForwardingItToTheAgent() throws Exception |
325 | 328 | } |
326 | 329 | } |
327 | 330 |
|
| 331 | + @Test |
| 332 | + void listenerShutdownWaitsForWebSocketAgentShutdown() throws Exception { |
| 333 | + Sinks.One<Void> allowAgentShutdown = Sinks.one(); |
| 334 | + CountDownLatch agentShutdownStarted = new CountDownLatch(1); |
| 335 | + AcpAgentFactory agentFactory = transport -> new BlockingCloseAgent(allowAgentShutdown, agentShutdownStarted); |
| 336 | + |
| 337 | + try (FixtureServer server = FixtureServer.start(agentFactory)) { |
| 338 | + MessageRecordingListener listener = new MessageRecordingListener(); |
| 339 | + HttpClient.newHttpClient() |
| 340 | + .newWebSocketBuilder() |
| 341 | + .connectTimeout(TIMEOUT) |
| 342 | + .buildAsync(server.endpoint(), listener) |
| 343 | + .get(TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); |
| 344 | + assertThat(listener.openLatch.await(TIMEOUT.toMillis(), TimeUnit.MILLISECONDS)).isTrue(); |
| 345 | + |
| 346 | + CompletableFuture<Void> shutdown = server.transport().closeGracefully().toFuture(); |
| 347 | + assertThat(agentShutdownStarted.await(TIMEOUT.toMillis(), TimeUnit.MILLISECONDS)).isTrue(); |
| 348 | + assertThat(shutdown).isNotDone(); |
| 349 | + |
| 350 | + allowAgentShutdown.tryEmitEmpty(); |
| 351 | + shutdown.get(TIMEOUT.toMillis(), TimeUnit.MILLISECONDS); |
| 352 | + } |
| 353 | + } |
| 354 | + |
328 | 355 | private static AcpAgentFactory simpleAgentFactory() { |
329 | 356 | AtomicInteger sessionCounter = new AtomicInteger(); |
330 | 357 | return AcpAgentFactory.async(transport -> AcpAgent.async(transport) |
@@ -462,4 +489,103 @@ public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, boolean |
462 | 489 |
|
463 | 490 | } |
464 | 491 |
|
| 492 | + private static final class BlockingCloseAgent implements AcpAsyncAgent { |
| 493 | + |
| 494 | + private final Sinks.One<Void> allowShutdown; |
| 495 | + |
| 496 | + private final CountDownLatch shutdownStarted; |
| 497 | + |
| 498 | + BlockingCloseAgent(Sinks.One<Void> allowShutdown, CountDownLatch shutdownStarted) { |
| 499 | + this.allowShutdown = allowShutdown; |
| 500 | + this.shutdownStarted = shutdownStarted; |
| 501 | + } |
| 502 | + |
| 503 | + @Override |
| 504 | + public Mono<Void> start() { |
| 505 | + return Mono.empty(); |
| 506 | + } |
| 507 | + |
| 508 | + @Override |
| 509 | + public Mono<Void> awaitTermination() { |
| 510 | + return Mono.never(); |
| 511 | + } |
| 512 | + |
| 513 | + @Override |
| 514 | + public NegotiatedCapabilities getClientCapabilities() { |
| 515 | + return null; |
| 516 | + } |
| 517 | + |
| 518 | + @Override |
| 519 | + public Mono<Void> sendSessionUpdate(String sessionId, AcpSchema.SessionUpdate update) { |
| 520 | + return unsupported(); |
| 521 | + } |
| 522 | + |
| 523 | + @Override |
| 524 | + public Mono<AcpSchema.RequestPermissionResponse> requestPermission(AcpSchema.RequestPermissionRequest request) { |
| 525 | + return unsupported(); |
| 526 | + } |
| 527 | + |
| 528 | + @Override |
| 529 | + public Mono<AcpSchema.ReadTextFileResponse> readTextFile(AcpSchema.ReadTextFileRequest request) { |
| 530 | + return unsupported(); |
| 531 | + } |
| 532 | + |
| 533 | + @Override |
| 534 | + public Mono<AcpSchema.WriteTextFileResponse> writeTextFile(AcpSchema.WriteTextFileRequest request) { |
| 535 | + return unsupported(); |
| 536 | + } |
| 537 | + |
| 538 | + @Override |
| 539 | + public Mono<AcpSchema.CreateTerminalResponse> createTerminal(AcpSchema.CreateTerminalRequest request) { |
| 540 | + return unsupported(); |
| 541 | + } |
| 542 | + |
| 543 | + @Override |
| 544 | + public Mono<AcpSchema.TerminalOutputResponse> getTerminalOutput(AcpSchema.TerminalOutputRequest request) { |
| 545 | + return unsupported(); |
| 546 | + } |
| 547 | + |
| 548 | + @Override |
| 549 | + public Mono<AcpSchema.ReleaseTerminalResponse> releaseTerminal(AcpSchema.ReleaseTerminalRequest request) { |
| 550 | + return unsupported(); |
| 551 | + } |
| 552 | + |
| 553 | + @Override |
| 554 | + public Mono<AcpSchema.WaitForTerminalExitResponse> waitForTerminalExit(AcpSchema.WaitForTerminalExitRequest request) { |
| 555 | + return unsupported(); |
| 556 | + } |
| 557 | + |
| 558 | + @Override |
| 559 | + public Mono<AcpSchema.KillTerminalCommandResponse> killTerminal(AcpSchema.KillTerminalCommandRequest request) { |
| 560 | + return unsupported(); |
| 561 | + } |
| 562 | + |
| 563 | + @Override |
| 564 | + public Mono<AcpSchema.CreateElicitationResponse> createElicitation(AcpSchema.CreateElicitationRequest request) { |
| 565 | + return unsupported(); |
| 566 | + } |
| 567 | + |
| 568 | + @Override |
| 569 | + public Mono<Void> completeElicitation(AcpSchema.CompleteElicitationNotification notification) { |
| 570 | + return unsupported(); |
| 571 | + } |
| 572 | + |
| 573 | + @Override |
| 574 | + public Mono<Void> closeGracefully() { |
| 575 | + return Mono.defer(() -> { |
| 576 | + shutdownStarted.countDown(); |
| 577 | + return allowShutdown.asMono(); |
| 578 | + }); |
| 579 | + } |
| 580 | + |
| 581 | + @Override |
| 582 | + public void close() { |
| 583 | + } |
| 584 | + |
| 585 | + private static <T> Mono<T> unsupported() { |
| 586 | + return Mono.error(new UnsupportedOperationException()); |
| 587 | + } |
| 588 | + |
| 589 | + } |
| 590 | + |
465 | 591 | } |
0 commit comments