Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
spotless
  • Loading branch information
edburns committed Mar 27, 2026
commit 075df313376abd8b77cf03176a6506828614cda9
1 change: 0 additions & 1 deletion src/main/java/com/github/copilot/sdk/CopilotSession.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,12 @@ public class SchedulerShutdownRaceTest {
@Test
void sendAndWaitShouldReturnFailedFutureWhenSchedulerIsShutDown() throws Exception {
// Build a session via reflection (package-private constructor)
var ctor = CopilotSession.class.getDeclaredConstructor(
String.class, JsonRpcClient.class, String.class);
var ctor = CopilotSession.class.getDeclaredConstructor(String.class, JsonRpcClient.class, String.class);
ctor.setAccessible(true);

// Mock JsonRpcClient so send() returns a pending future instead of NPE
var mockRpc = mock(JsonRpcClient.class);
when(mockRpc.invoke(any(), any(), any()))
.thenReturn(new CompletableFuture<>());
when(mockRpc.invoke(any(), any(), any())).thenReturn(new CompletableFuture<>());

var session = ctor.newInstance("race-test", mockRpc, null);

Expand All @@ -54,12 +52,10 @@ void sendAndWaitShouldReturnFailedFutureWhenSchedulerIsShutDown() throws Excepti

// With the fix: sendAndWait returns a future that completes exceptionally.
// Without the fix: sendAndWait throws RejectedExecutionException directly.
CompletableFuture<?> result = session.sendAndWait(
new MessageOptions().setPrompt("test"), 5000);
CompletableFuture<?> result = session.sendAndWait(new MessageOptions().setPrompt("test"), 5000);

assertNotNull(result, "sendAndWait should return a future, not throw");
var ex = assertThrows(ExecutionException.class,
() -> result.get(1, TimeUnit.SECONDS));
var ex = assertThrows(ExecutionException.class, () -> result.get(1, TimeUnit.SECONDS));
assertInstanceOf(RejectedExecutionException.class, ex.getCause());
}
}
16 changes: 5 additions & 11 deletions src/test/java/com/github/copilot/sdk/ZeroTimeoutContractTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,10 @@
import static org.mockito.Mockito.*;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.junit.jupiter.api.Test;

import com.github.copilot.sdk.events.AssistantMessageEvent;
import com.github.copilot.sdk.events.SessionIdleEvent;
import com.github.copilot.sdk.json.MessageOptions;

/**
Expand All @@ -28,26 +25,23 @@ public class ZeroTimeoutContractTest {
@Test
void sendAndWaitWithZeroTimeoutShouldNotTimeOut() throws Exception {
// Build a session via reflection (package-private constructor)
var ctor = CopilotSession.class.getDeclaredConstructor(
String.class, JsonRpcClient.class, String.class);
var ctor = CopilotSession.class.getDeclaredConstructor(String.class, JsonRpcClient.class, String.class);
ctor.setAccessible(true);

var mockRpc = mock(JsonRpcClient.class);
when(mockRpc.invoke(any(), any(), any()))
.thenReturn(new CompletableFuture<>());
when(mockRpc.invoke(any(), any(), any())).thenReturn(new CompletableFuture<>());

var session = ctor.newInstance("zero-timeout-test", mockRpc, null);

// Per the Javadoc: timeoutMs of 0 means "no timeout".
// The future should NOT complete with TimeoutException.
CompletableFuture<AssistantMessageEvent> result =
session.sendAndWait(new MessageOptions().setPrompt("test"), 0);
CompletableFuture<AssistantMessageEvent> result = session.sendAndWait(new MessageOptions().setPrompt("test"),
0);

// Give the scheduler a chance to fire if it was (incorrectly) scheduled
Thread.sleep(200);

// The future should still be pending — not timed out
assertFalse(result.isDone(),
"Future should not be done; timeoutMs=0 means no timeout per Javadoc");
assertFalse(result.isDone(), "Future should not be done; timeoutMs=0 means no timeout per Javadoc");
}
}
Loading