fix(test): improve timing stability in tests (#506)
This commit is contained in:
parent
9d4535b53f
commit
7eb2a93c46
2 changed files with 66 additions and 35 deletions
|
|
@ -1,5 +1,6 @@
|
|||
package io.modelcontextprotocol.kotlin.sdk.client
|
||||
|
||||
import io.kotest.assertions.nondeterministic.eventually
|
||||
import io.kotest.assertions.throwables.shouldThrow
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.string.shouldContain
|
||||
|
|
@ -13,6 +14,7 @@ import kotlinx.coroutines.test.runTest
|
|||
import kotlin.test.BeforeTest
|
||||
import kotlin.test.Test
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
abstract class AbstractClientTransportLifecycleTest<T : AbstractTransport> {
|
||||
|
||||
|
|
@ -60,11 +62,15 @@ abstract class AbstractClientTransportLifecycleTest<T : AbstractTransport> {
|
|||
val transport = createTransport()
|
||||
|
||||
transport.start()
|
||||
// Give transport time to initialize
|
||||
delay(50.milliseconds)
|
||||
transport.close()
|
||||
|
||||
shouldThrow<Exception> {
|
||||
transport.send(PingRequest().toJSON())
|
||||
// Verify that send throws after close using eventually for reliability
|
||||
eventually(2.seconds) {
|
||||
shouldThrow<Exception> {
|
||||
transport.send(PingRequest().toJSON())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -76,13 +82,17 @@ abstract class AbstractClientTransportLifecycleTest<T : AbstractTransport> {
|
|||
transport.onClose { closeCallCount++ }
|
||||
|
||||
transport.start()
|
||||
// Give transport time to initialize
|
||||
delay(50.milliseconds)
|
||||
|
||||
// Multiple close attempts
|
||||
transport.close()
|
||||
transport.close()
|
||||
|
||||
closeCallCount shouldBe 1
|
||||
// Verify onClose was called exactly once using eventually for cross-platform reliability
|
||||
eventually(2.seconds) {
|
||||
closeCallCount shouldBe 1
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun createTransport(): T
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package io.modelcontextprotocol.kotlin.sdk.client.stdio
|
||||
|
||||
import io.kotest.assertions.nondeterministic.eventually
|
||||
import io.kotest.assertions.throwables.shouldThrow
|
||||
import io.kotest.matchers.booleans.shouldBeFalse
|
||||
import io.kotest.matchers.shouldBe
|
||||
|
|
@ -27,7 +28,7 @@ import java.util.stream.Stream
|
|||
import kotlin.concurrent.atomics.AtomicBoolean
|
||||
import kotlin.concurrent.atomics.ExperimentalAtomicApi
|
||||
import kotlin.test.Test
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
/**
|
||||
* Tests for StdioClientTransport error handling: EOF, IO errors, and edge cases.
|
||||
|
|
@ -57,10 +58,13 @@ class StdioClientTransportErrorHandlingTest {
|
|||
transport.onClose { closeCalled.store(true) }
|
||||
|
||||
transport.start()
|
||||
delay(200.milliseconds)
|
||||
|
||||
// Stderr EOF should not close transport
|
||||
closeCalled.load() shouldBe false
|
||||
// Use eventually to handle timing differences across platforms (especially Windows)
|
||||
eventually(2.seconds) {
|
||||
// Wait for stderr to be processed, then verify transport is still open
|
||||
closeCalled.load() shouldBe false
|
||||
}
|
||||
|
||||
transport.close()
|
||||
closeCalled.load() shouldBe true
|
||||
|
|
@ -87,9 +91,13 @@ class StdioClientTransportErrorHandlingTest {
|
|||
transport.onClose { closeCallCount++ }
|
||||
|
||||
transport.start()
|
||||
delay(100.milliseconds)
|
||||
|
||||
// Explicit close after error already closed it
|
||||
// FATAL stderr should trigger close, wait for it to complete
|
||||
eventually(2.seconds) {
|
||||
closeCallCount shouldBe 1
|
||||
}
|
||||
|
||||
// Explicit close after error already closed it (should be no-op)
|
||||
transport.close()
|
||||
|
||||
closeCallCount shouldBe 1
|
||||
|
|
@ -109,10 +117,12 @@ class StdioClientTransportErrorHandlingTest {
|
|||
transport.onError { errorCalled = true }
|
||||
|
||||
transport.start()
|
||||
delay(100.milliseconds)
|
||||
|
||||
// Empty input should close cleanly without error
|
||||
errorCalled.shouldBeFalse()
|
||||
// Wait for EOF processing to complete, verify no error was called
|
||||
eventually(2.seconds) {
|
||||
errorCalled.shouldBeFalse()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
|||
Loading…
Reference in a new issue