Skip to content

Commit 69b0fee

Browse files
committed
fixup! Introduce Kotlin integration tests
Signed-off-by: Sergey Karpov <[email protected]>
1 parent bcf97a0 commit 69b0fee

File tree

3 files changed

+53
-80
lines changed

3 files changed

+53
-80
lines changed

kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/typescript/KotlinClientTypeScriptServerEdgeCasesTest.kt

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -30,27 +30,12 @@ class KotlinClientTypeScriptServerEdgeCasesTest : TypeScriptTestBase() {
3030
fun setUp() {
3131
port = findFreePort()
3232
serverUrl = "http://$host:$port/mcp"
33-
killProcessOnPort(port)
34-
println("Starting TypeScript server on port $port")
35-
val processBuilder = ProcessBuilder()
36-
.command("bash", "-c", "MCP_PORT=$port npx tsx src/examples/server/simpleStreamableHttp.ts")
37-
.directory(sdkDir)
38-
.redirectErrorStream(true)
39-
40-
tsServerProcess = processBuilder.start()
41-
if (!waitForPort(host, port, 10)) {
42-
throw IllegalStateException("TypeScript server did not become ready on $host:$port within timeout")
43-
}
33+
tsServerProcess = startTypeScriptServer(port)
4434
println("TypeScript server started on port $port")
45-
46-
// print TypeScript server process output
47-
val outputReader = createProcessOutputReader(tsServerProcess, "TS-SERVER")
48-
outputReader.start()
4935
}
5036

5137
@AfterEach
5238
fun tearDown() {
53-
// close the client
5439
if (::client.isInitialized) {
5540
try {
5641
runBlocking {
@@ -63,16 +48,10 @@ class KotlinClientTypeScriptServerEdgeCasesTest : TypeScriptTestBase() {
6348
}
6449
}
6550

66-
// terminate TypeScript server
6751
if (::tsServerProcess.isInitialized) {
6852
try {
6953
println("Stopping TypeScript server")
70-
tsServerProcess.destroy()
71-
if (waitForProcessTermination(tsServerProcess, 3)) {
72-
println("TypeScript server stopped gracefully")
73-
} else {
74-
println("TypeScript server did not stop gracefully, forced termination")
75-
}
54+
stopProcess(tsServerProcess, 3, "TypeScript server")
7655
} catch (e: Exception) {
7756
println("Warning: Error during TypeScript server stop: ${e.message}")
7857
}

kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/typescript/KotlinClientTypeScriptServerTest.kt

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -35,27 +35,12 @@ class KotlinClientTypeScriptServerTest : TypeScriptTestBase() {
3535
fun setUp() {
3636
port = findFreePort()
3737
serverUrl = "http://$host:$port/mcp"
38-
killProcessOnPort(port)
39-
println("Starting TypeScript server on port $port")
40-
val processBuilder = ProcessBuilder()
41-
.command("bash", "-c", "MCP_PORT=$port npx tsx src/examples/server/simpleStreamableHttp.ts")
42-
.directory(sdkDir)
43-
.redirectErrorStream(true)
44-
45-
tsServerProcess = processBuilder.start()
46-
if (!waitForPort(host, port, 10)) {
47-
throw IllegalStateException("TypeScript server did not become ready on $host:$port within timeout")
48-
}
38+
tsServerProcess = startTypeScriptServer(port)
4939
println("TypeScript server started on port $port")
50-
51-
// print TypeScript server process output
52-
val outputReader = createProcessOutputReader(tsServerProcess, "TS-SERVER")
53-
outputReader.start()
5440
}
5541

5642
@AfterEach
5743
fun tearDown() {
58-
// close the client
5944
if (::client.isInitialized) {
6045
try {
6146
runBlocking {
@@ -68,16 +53,10 @@ class KotlinClientTypeScriptServerTest : TypeScriptTestBase() {
6853
}
6954
}
7055

71-
// terminate TypeScript server
7256
if (::tsServerProcess.isInitialized) {
7357
try {
7458
println("Stopping TypeScript server")
75-
tsServerProcess.destroy()
76-
if (waitForProcessTermination(tsServerProcess, 3)) {
77-
println("TypeScript server stopped gracefully")
78-
} else {
79-
println("TypeScript server did not stop gracefully, forced termination")
80-
}
59+
stopProcess(tsServerProcess, 3, "TypeScript server")
8160
} catch (e: Exception) {
8261
println("Warning: Error during TypeScript server stop: ${e.message}")
8362
}

kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/typescript/TypeScriptTestBase.kt

Lines changed: 49 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,6 @@ abstract class TypeScriptTestBase {
2121
@JvmStatic
2222
protected val sdkDir: File = File(tempRootDir, "typescript-sdk")
2323

24-
/**
25-
* clone TypeScript SDK and install dependencies
26-
*/
2724
@JvmStatic
2825
@BeforeAll
2926
fun setupTypeScriptSdk() {
@@ -46,8 +43,27 @@ abstract class TypeScriptTestBase {
4643
}
4744

4845
@JvmStatic
49-
protected fun executeCommand(command: String, workingDir: File): String {
50-
// Prefer running TypeScript via ts-node to avoid npx network delays on CI
46+
protected fun executeCommand(command: String, workingDir: File): String =
47+
runCommand(command, workingDir, allowFailure = false, timeoutSeconds = null)
48+
49+
@JvmStatic
50+
protected fun killProcessOnPort(port: Int) {
51+
executeCommand("lsof -ti:$port | xargs kill -9 2>/dev/null || true", File("."))
52+
}
53+
54+
@JvmStatic
55+
protected fun findFreePort(): Int {
56+
ServerSocket(0).use { socket ->
57+
return socket.localPort
58+
}
59+
}
60+
61+
private fun runCommand(
62+
command: String,
63+
workingDir: File,
64+
allowFailure: Boolean,
65+
timeoutSeconds: Long?
66+
): String {
5167
val process = ProcessBuilder()
5268
.command("bash", "-c", "TYPESCRIPT_SDK_DIR='${sdkDir.absolutePath}' $command")
5369
.directory(workingDir)
@@ -63,25 +79,17 @@ abstract class TypeScriptTestBase {
6379
}
6480
}
6581

66-
val exitCode = process.waitFor()
67-
if (exitCode != 0) {
68-
throw RuntimeException("Command execution failed with exit code $exitCode: $command\nOutput:\n$output")
82+
if (timeoutSeconds == null) {
83+
val exitCode = process.waitFor()
84+
if (!allowFailure && exitCode != 0) {
85+
throw RuntimeException("Command execution failed with exit code $exitCode: $command\nOutput:\n$output")
86+
}
87+
} else {
88+
process.waitFor(timeoutSeconds, TimeUnit.SECONDS)
6989
}
7090

7191
return output.toString()
7292
}
73-
74-
@JvmStatic
75-
protected fun killProcessOnPort(port: Int) {
76-
executeCommand("lsof -ti:$port | xargs kill -9 2>/dev/null || true", File("."))
77-
}
78-
79-
@JvmStatic
80-
protected fun findFreePort(): Int {
81-
ServerSocket(0).use { socket ->
82-
return socket.localPort
83-
}
84-
}
8593
}
8694

8795
protected fun waitForProcessTermination(process: Process, timeoutSeconds: Long): Boolean {
@@ -121,22 +129,29 @@ abstract class TypeScriptTestBase {
121129
return false
122130
}
123131

124-
protected fun executeCommandAllowingFailure(command: String, workingDir: File, timeoutSeconds: Long = 20): String {
125-
val process = ProcessBuilder()
126-
.command("bash", "-c", "TYPESCRIPT_SDK_DIR='${sdkDir.absolutePath}' $command")
127-
.directory(workingDir)
128-
.redirectErrorStream(true)
129-
.start()
132+
protected fun executeCommandAllowingFailure(command: String, workingDir: File, timeoutSeconds: Long = 20): String =
133+
runCommand(command, workingDir, allowFailure = true, timeoutSeconds = timeoutSeconds)
130134

131-
val output = StringBuilder()
132-
process.inputStream.bufferedReader().useLines { lines ->
133-
for (line in lines) {
134-
println(line)
135-
output.append(line).append("\n")
136-
}
135+
protected fun startTypeScriptServer(port: Int): Process {
136+
killProcessOnPort(port)
137+
val processBuilder = ProcessBuilder()
138+
.command("bash", "-c", "MCP_PORT=$port npx tsx src/examples/server/simpleStreamableHttp.ts")
139+
.directory(sdkDir)
140+
.redirectErrorStream(true)
141+
val process = processBuilder.start()
142+
if (!waitForPort("localhost", port, 10)) {
143+
throw IllegalStateException("TypeScript server did not become ready on localhost:$port within timeout")
137144
}
145+
createProcessOutputReader(process, "TS-SERVER").start()
146+
return process
147+
}
138148

139-
process.waitFor(timeoutSeconds, TimeUnit.SECONDS)
140-
return output.toString()
149+
protected fun stopProcess(process: Process, waitSeconds: Long = 3, name: String = "process") {
150+
process.destroy()
151+
if (waitForProcessTermination(process, waitSeconds)) {
152+
println("$name stopped gracefully")
153+
} else {
154+
println("$name did not stop gracefully, forced termination")
155+
}
141156
}
142157
}

0 commit comments

Comments
 (0)