Skip to content

Commit 7603468

Browse files
committed
Join server threads before closing IO pipes in stdio client tests
Background threads simulating server responses could race with the `ensure` block that closes IO pipes, intermittently producing this warning depending on thread scheduling: ```console #<Thread:0x00007fb69290cbd0 test/mcp/client/stdio_test.rb:28 run> terminated with exception (report_on_exception is true): test/mcp/client/stdio_test.rb:55:in `write': stream closed in another thread (IOError) ``` Store each background thread in a `server_thread` variable and call `server_thread&.join(1)` at the top of each `ensure` block to wait for the thread to finish before closing pipes.
1 parent 03a78f9 commit 7603468

File tree

1 file changed

+18
-9
lines changed

1 file changed

+18
-9
lines changed

test/mcp/client/stdio_test.rb

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def test_send_request_starts_process_and_returns_response
2525
}
2626

2727
# Simulate server responses: initialize response, then tools/list response
28-
Thread.new do
28+
server_thread = Thread.new do
2929
# Read and respond to initialize request
3030
init_line = stdin_read.gets
3131
init_request = JSON.parse(init_line)
@@ -62,6 +62,7 @@ def test_send_request_starts_process_and_returns_response
6262
assert_equal(1, response.dig("result", "tools").size)
6363
assert_equal("test_tool", response.dig("result", "tools", 0, "name"))
6464
ensure
65+
server_thread.join
6566
stdin_read.close
6667
stdin_write.close
6768
stdout_read.close
@@ -85,7 +86,7 @@ def test_send_request_initializes_session_on_first_call
8586

8687
received_methods = []
8788

88-
Thread.new do
89+
server_thread = Thread.new do
8990
# Read initialize request
9091
init_line = stdin_read.gets
9192
init_request = JSON.parse(init_line)
@@ -126,6 +127,7 @@ def test_send_request_initializes_session_on_first_call
126127

127128
assert_equal(["initialize", "notifications/initialized", "tools/list"], received_methods)
128129
ensure
130+
server_thread.join
129131
stdin_read.close
130132
stdin_write.close
131133
stdout_read.close
@@ -147,7 +149,7 @@ def test_send_request_skips_notifications
147149
method: "tools/list",
148150
}
149151

150-
Thread.new do
152+
server_thread = Thread.new do
151153
# Handle initialize handshake
152154
init_line = stdin_read.gets
153155
init_request = JSON.parse(init_line)
@@ -187,6 +189,7 @@ def test_send_request_skips_notifications
187189
assert_equal("test-id", response["id"])
188190
assert_equal([], response.dig("result", "tools"))
189191
ensure
192+
server_thread.join
190193
stdin_read.close
191194
stdin_write.close
192195
stdout_read.close
@@ -242,7 +245,7 @@ def test_send_request_raises_error_on_closed_stdout
242245
method: "tools/list",
243246
}
244247

245-
Thread.new do
248+
server_thread = Thread.new do
246249
# Handle initialize handshake
247250
init_line = stdin_read.gets
248251
init_request = JSON.parse(init_line)
@@ -272,6 +275,7 @@ def test_send_request_raises_error_on_closed_stdout
272275
assert_equal("Server process closed stdout unexpectedly", error.message)
273276
assert_equal(:internal_error, error.error_type)
274277
ensure
278+
server_thread.join
275279
stdin_read.close
276280
stdin_write.close
277281
stdout_read.close
@@ -330,7 +334,7 @@ def test_send_request_skips_initialization_on_second_call
330334

331335
received_methods = []
332336

333-
Thread.new do
337+
server_thread = Thread.new do
334338
# First call: initialize handshake
335339
init_line = stdin_read.gets
336340
init_request = JSON.parse(init_line)
@@ -384,6 +388,7 @@ def test_send_request_skips_initialization_on_second_call
384388
received_methods,
385389
)
386390
ensure
391+
server_thread.join
387392
stdin_read.close
388393
stdin_write.close
389394
stdout_read.close
@@ -415,7 +420,7 @@ def test_send_request_raises_error_on_invalid_json
415420
method: "tools/list",
416421
}
417422

418-
Thread.new do
423+
server_thread = Thread.new do
419424
# Handle initialize handshake
420425
init_line = stdin_read.gets
421426
init_request = JSON.parse(init_line)
@@ -447,6 +452,7 @@ def test_send_request_raises_error_on_invalid_json
447452
assert_equal(:internal_error, error.error_type)
448453
assert_instance_of(JSON::ParserError, error.original_error)
449454
ensure
455+
server_thread.join
450456
stdin_read.close
451457
stdin_write.close
452458
stdout_read.close
@@ -468,7 +474,7 @@ def test_send_request_raises_error_when_initialization_fails
468474
method: "tools/list",
469475
}
470476

471-
Thread.new do
477+
server_thread = Thread.new do
472478
# Read initialize request and return an error
473479
init_line = stdin_read.gets
474480
init_request = JSON.parse(init_line)
@@ -487,6 +493,7 @@ def test_send_request_raises_error_when_initialization_fails
487493
assert_equal("Server initialization failed: Invalid Request", error.message)
488494
assert_equal(:internal_error, error.error_type)
489495
ensure
496+
server_thread.join
490497
stdin_read.close
491498
stdin_write.close
492499
stdout_read.close
@@ -534,7 +541,7 @@ def test_read_response_raises_error_on_timeout
534541
method: "tools/list",
535542
}
536543

537-
Thread.new do
544+
server_thread = Thread.new do
538545
# Handle initialize handshake
539546
init_line = stdin_read.gets
540547
init_request = JSON.parse(init_line)
@@ -563,6 +570,7 @@ def test_read_response_raises_error_on_timeout
563570
assert_equal("Timed out waiting for server response", error.message)
564571
assert_equal(:internal_error, error.error_type)
565572
ensure
573+
server_thread.join
566574
stdin_read.close
567575
stdin_write.close
568576
stdout_read.close
@@ -689,7 +697,7 @@ def test_send_request_raises_error_for_missing_result
689697
method: "tools/list",
690698
}
691699

692-
Thread.new do
700+
server_thread = Thread.new do
693701
# Read initialize request and return a response without result
694702
init_line = stdin_read.gets
695703
init_request = JSON.parse(init_line)
@@ -707,6 +715,7 @@ def test_send_request_raises_error_for_missing_result
707715
assert_equal("Server initialization failed: missing result in response", error.message)
708716
assert_equal(:internal_error, error.error_type)
709717
ensure
718+
server_thread.join
710719
stdin_read.close
711720
stdin_write.close
712721
stdout_read.close

0 commit comments

Comments
 (0)