Skip to content

Commit aef332d

Browse files
committed
test: add unit tests for CliServerManager to improve coverage and validate functionality
1 parent 3f462f8 commit aef332d

1 file changed

Lines changed: 215 additions & 0 deletions

File tree

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
*--------------------------------------------------------------------------------------------*/
4+
5+
package com.github.copilot.sdk;
6+
7+
import static org.junit.jupiter.api.Assertions.*;
8+
9+
import java.io.IOException;
10+
import java.net.ServerSocket;
11+
import java.net.URI;
12+
13+
import org.junit.jupiter.api.Test;
14+
15+
import com.github.copilot.sdk.json.CopilotClientOptions;
16+
17+
/**
18+
* Unit tests for {@link CliServerManager} covering parseCliUrl,
19+
* connectToServer, resolveCliCommand, and ProcessInfo coverage gaps identified
20+
* by JaCoCo.
21+
*/
22+
class CliServerManagerTest {
23+
24+
// ===== parseCliUrl tests =====
25+
26+
@Test
27+
void parseCliUrlWithPortNumber() {
28+
URI uri = CliServerManager.parseCliUrl("8080");
29+
assertEquals("http://localhost:8080", uri.toString());
30+
}
31+
32+
@Test
33+
void parseCliUrlWithHostColonPort() {
34+
URI uri = CliServerManager.parseCliUrl("myhost:9090");
35+
assertEquals("https://myhost:9090", uri.toString());
36+
}
37+
38+
@Test
39+
void parseCliUrlWithHttpPrefix() {
40+
URI uri = CliServerManager.parseCliUrl("http://example.com:3000");
41+
assertEquals("http://example.com:3000", uri.toString());
42+
}
43+
44+
@Test
45+
void parseCliUrlWithHttpsPrefix() {
46+
URI uri = CliServerManager.parseCliUrl("https://secure.host:443");
47+
assertEquals("https://secure.host:443", uri.toString());
48+
}
49+
50+
@Test
51+
void parseCliUrlWithHostOnly() {
52+
URI uri = CliServerManager.parseCliUrl("copilot.example.com");
53+
assertEquals("https://copilot.example.com", uri.toString());
54+
}
55+
56+
// ===== connectToServer tests =====
57+
58+
@Test
59+
void connectToServerTcpMode() throws Exception {
60+
var options = new CopilotClientOptions();
61+
var manager = new CliServerManager(options);
62+
63+
// Start a temporary server socket to connect to
64+
try (ServerSocket ss = new ServerSocket(0)) {
65+
int port = ss.getLocalPort();
66+
JsonRpcClient client = manager.connectToServer(null, "localhost", port);
67+
assertNotNull(client);
68+
client.close();
69+
}
70+
}
71+
72+
@Test
73+
void connectToServerStdioMode() throws Exception {
74+
var options = new CopilotClientOptions();
75+
var manager = new CliServerManager(options);
76+
77+
// Create a dummy process for stdio mode
78+
Process process = new ProcessBuilder("cat").start();
79+
try {
80+
JsonRpcClient client = manager.connectToServer(process, null, null);
81+
assertNotNull(client);
82+
client.close();
83+
} finally {
84+
process.destroyForcibly();
85+
}
86+
}
87+
88+
@Test
89+
void connectToServerNoProcessNoHost() {
90+
var options = new CopilotClientOptions();
91+
var manager = new CliServerManager(options);
92+
93+
var ex = assertThrows(IllegalStateException.class, () -> manager.connectToServer(null, null, null));
94+
assertTrue(ex.getMessage().contains("Cannot connect"));
95+
}
96+
97+
@Test
98+
void connectToServerNullHostNonNullPort() {
99+
var options = new CopilotClientOptions();
100+
var manager = new CliServerManager(options);
101+
102+
// tcpHost is null but tcpPort is non-null → falls to process check → process
103+
// null → exception
104+
var ex = assertThrows(IllegalStateException.class, () -> manager.connectToServer(null, null, 8080));
105+
assertTrue(ex.getMessage().contains("Cannot connect"));
106+
}
107+
108+
// ===== ProcessInfo record tests =====
109+
110+
@Test
111+
void processInfoRecord() {
112+
var info = new CliServerManager.ProcessInfo(null, 12345);
113+
assertNull(info.process());
114+
assertEquals(12345, info.port());
115+
}
116+
117+
@Test
118+
void processInfoWithNullPort() {
119+
var info = new CliServerManager.ProcessInfo(null, null);
120+
assertNull(info.process());
121+
assertNull(info.port());
122+
}
123+
124+
// ===== resolveCliCommand tests (via startCliServer) =====
125+
// resolveCliCommand is private, so we test indirectly through startCliServer
126+
// with specific cliPath values.
127+
128+
@Test
129+
void startCliServerWithJsFile() throws Exception {
130+
// Using a .js file path causes resolveCliCommand to prepend "node"
131+
// node is on PATH so the process starts, but the script doesn't exist
132+
// so node exits quickly — verifying the .js branch was taken
133+
var options = new CopilotClientOptions().setCliPath("/nonexistent/script.js").setUseStdio(true);
134+
var manager = new CliServerManager(options);
135+
136+
try {
137+
var info = manager.startCliServer();
138+
// If process started, clean it up
139+
info.process().destroyForcibly();
140+
} catch (IOException e) {
141+
// Expected — node may fail or not be present; either way the branch is hit
142+
assertNotNull(e);
143+
}
144+
}
145+
146+
@Test
147+
void startCliServerWithCliArgs() throws Exception {
148+
// Test that cliArgs are included in the command
149+
var options = new CopilotClientOptions().setCliPath("/nonexistent/copilot")
150+
.setCliArgs(new String[]{"--extra-flag"}).setUseStdio(true);
151+
var manager = new CliServerManager(options);
152+
153+
var ex = assertThrows(IOException.class, () -> manager.startCliServer());
154+
assertNotNull(ex);
155+
}
156+
157+
@Test
158+
void startCliServerWithExplicitPort() throws Exception {
159+
// Test the explicit port branch (useStdio=false, port > 0)
160+
var options = new CopilotClientOptions().setCliPath("/nonexistent/copilot").setUseStdio(false).setPort(9999);
161+
var manager = new CliServerManager(options);
162+
163+
var ex = assertThrows(IOException.class, () -> manager.startCliServer());
164+
assertNotNull(ex);
165+
}
166+
167+
@Test
168+
void startCliServerWithGithubToken() throws Exception {
169+
// Test the github token branch
170+
var options = new CopilotClientOptions().setCliPath("/nonexistent/copilot").setGithubToken("ghp_test123")
171+
.setUseStdio(true);
172+
var manager = new CliServerManager(options);
173+
174+
var ex = assertThrows(IOException.class, () -> manager.startCliServer());
175+
assertNotNull(ex);
176+
}
177+
178+
@Test
179+
void startCliServerWithUseLoggedInUserExplicit() throws Exception {
180+
// Test the explicit useLoggedInUser=false branch (adds --no-auto-login)
181+
var options = new CopilotClientOptions().setCliPath("/nonexistent/copilot").setUseLoggedInUser(false)
182+
.setUseStdio(true);
183+
var manager = new CliServerManager(options);
184+
185+
var ex = assertThrows(IOException.class, () -> manager.startCliServer());
186+
assertNotNull(ex);
187+
}
188+
189+
@Test
190+
void startCliServerWithGithubTokenAndNoExplicitUseLoggedInUser() throws Exception {
191+
// When githubToken is set and useLoggedInUser is null, defaults to false
192+
var options = new CopilotClientOptions().setCliPath("/nonexistent/copilot").setGithubToken("ghp_test123")
193+
.setUseStdio(true);
194+
var manager = new CliServerManager(options);
195+
196+
var ex = assertThrows(IOException.class, () -> manager.startCliServer());
197+
assertNotNull(ex);
198+
}
199+
200+
@Test
201+
void startCliServerWithNullCliPath() throws Exception {
202+
// Test the null cliPath branch (defaults to "copilot")
203+
var options = new CopilotClientOptions().setCliPath(null).setUseStdio(true);
204+
var manager = new CliServerManager(options);
205+
206+
// "copilot" likely doesn't exist in the test env — that's fine
207+
try {
208+
var info = manager.startCliServer();
209+
info.process().destroyForcibly();
210+
} catch (IOException e) {
211+
// Expected if "copilot" is not on PATH
212+
assertNotNull(e);
213+
}
214+
}
215+
}

0 commit comments

Comments
 (0)