@@ -27,43 +27,36 @@ public async Task AutoDetect_Should_Use_StreamableHttp_When_Server_Supports_It()
2727 using var httpClient = new HttpClient ( mockHttpHandler ) ;
2828 await using var transport = new SseClientTransport ( options , httpClient , LoggerFactory ) ;
2929
30- var requestCount = 0 ;
30+ // Simulate successful Streamable HTTP response for initialize
3131 mockHttpHandler . RequestHandler = ( request ) =>
3232 {
33- requestCount ++ ;
34-
35- // Simulate successful Streamable HTTP response
36- return Task . FromResult ( new HttpResponseMessage
33+ if ( request . Method == HttpMethod . Post )
3734 {
38- StatusCode = HttpStatusCode . OK ,
39- Content = new StringContent ( "{\" jsonrpc\" :\" 2.0\" ,\" id\" :\" test-id\" ,\" result\" :{}}" ) ,
40- Headers =
35+ return Task . FromResult ( new HttpResponseMessage
4136 {
42- { "Content-Type" , "application/json" } ,
43- { "mcp-session-id" , "test-session" }
44- }
45- } ) ;
37+ StatusCode = HttpStatusCode . OK ,
38+ Content = new StringContent ( "{\" jsonrpc\" :\" 2.0\" ,\" id\" :\" init-id\" ,\" result\" :{\" protocolVersion\" :\" 2024-11-05\" ,\" capabilities\" :{\" tools\" :{}}}}" ) ,
39+ Headers =
40+ {
41+ { "Content-Type" , "application/json" } ,
42+ { "mcp-session-id" , "test-session" }
43+ }
44+ } ) ;
45+ }
46+
47+ // Shouldn't reach here for successful Streamable HTTP
48+ throw new InvalidOperationException ( "Unexpected request" ) ;
4649 } ;
4750
4851 await using var session = await transport . ConnectAsync ( TestContext . Current . CancellationToken ) ;
4952
50- // The auto-detecting transport should be returned and connected
53+ // The auto-detecting transport should be returned
5154 Assert . NotNull ( session ) ;
5255 Assert . True ( session . IsConnected ) ;
5356 Assert . IsType < AutoDetectingClientTransport > ( session ) ;
54-
55- // Send a test message to trigger the transport selection
56- await session . SendMessageAsync ( new JsonRpcRequest
57- {
58- Method = RequestMethods . Initialize ,
59- Id = new RequestId ( "test-id" )
60- } , CancellationToken . None ) ;
61-
62- // Verify that we only made one request (Streamable HTTP worked)
63- Assert . Equal ( 1 , requestCount ) ;
6457 }
6558
66- [ Fact ]
59+ [ Fact ]
6760 public async Task AutoDetect_Should_Fallback_To_Sse_When_StreamableHttp_Fails ( )
6861 {
6962 var options = new SseClientTransportOptions
@@ -79,26 +72,24 @@ public async Task AutoDetect_Should_Fallback_To_Sse_When_StreamableHttp_Fails()
7972 await using var transport = new SseClientTransport ( options , httpClient , LoggerFactory ) ;
8073
8174 var requestCount = 0 ;
82- var isFirstRequest = true ;
8375
8476 mockHttpHandler . RequestHandler = ( request ) =>
8577 {
8678 requestCount ++ ;
8779
88- if ( isFirstRequest && request . Method == HttpMethod . Post )
80+ if ( request . Method == HttpMethod . Post && requestCount == 1 )
8981 {
90- isFirstRequest = false ;
91- // Simulate Streamable HTTP failure (e.g., 404 Not Found)
82+ // First POST (Streamable HTTP) fails
9283 return Task . FromResult ( new HttpResponseMessage
9384 {
9485 StatusCode = HttpStatusCode . NotFound ,
9586 Content = new StringContent ( "Streamable HTTP not supported" )
9687 } ) ;
9788 }
9889
99- // Simulate SSE endpoint response for GET request
10090 if ( request . Method == HttpMethod . Get )
10191 {
92+ // SSE connection request
10293 return Task . FromResult ( new HttpResponseMessage
10394 {
10495 StatusCode = HttpStatusCode . OK ,
@@ -107,33 +98,29 @@ public async Task AutoDetect_Should_Fallback_To_Sse_When_StreamableHttp_Fails()
10798 } ) ;
10899 }
109100
110- // Simulate successful SSE POST response
111- return Task . FromResult ( new HttpResponseMessage
101+ if ( request . Method == HttpMethod . Post && requestCount > 1 )
112102 {
113- StatusCode = HttpStatusCode . OK ,
114- Content = new StringContent ( "accepted" )
115- } ) ;
103+ // Subsequent POST to SSE endpoint succeeds
104+ return Task . FromResult ( new HttpResponseMessage
105+ {
106+ StatusCode = HttpStatusCode . OK ,
107+ Content = new StringContent ( "accepted" )
108+ } ) ;
109+ }
110+
111+ throw new InvalidOperationException ( $ "Unexpected request: { request . Method } , count: { requestCount } ") ;
116112 } ;
117113
118114 await using var session = await transport . ConnectAsync ( TestContext . Current . CancellationToken ) ;
119115
120116 // The auto-detecting transport should be returned
121117 Assert . NotNull ( session ) ;
118+ Assert . True ( session . IsConnected ) ;
122119 Assert . IsType < AutoDetectingClientTransport > ( session ) ;
123-
124- // Send a test message to trigger the transport selection and fallback
125- await session . SendMessageAsync ( new JsonRpcRequest
126- {
127- Method = RequestMethods . Initialize ,
128- Id = new RequestId ( "test-id" )
129- } , CancellationToken . None ) ;
130-
131- // Verify that we made multiple requests (Streamable HTTP failed, SSE succeeded)
132- Assert . True ( requestCount >= 2 , $ "Expected at least 2 requests, but got { requestCount } ") ;
133120 }
134121
135122 [ Fact ]
136- public async Task AutoDetect_Should_Be_Default_When_UseStreamableHttp_Is_False ( )
123+ public async Task UseStreamableHttp_False_Should_Default_To_AutoDetect ( )
137124 {
138125 var options = new SseClientTransportOptions
139126 {
@@ -147,9 +134,9 @@ public async Task AutoDetect_Should_Be_Default_When_UseStreamableHttp_Is_False()
147134 using var httpClient = new HttpClient ( mockHttpHandler ) ;
148135 await using var transport = new SseClientTransport ( options , httpClient , LoggerFactory ) ;
149136
137+ // Configure for successful Streamable HTTP response
150138 mockHttpHandler . RequestHandler = ( request ) =>
151139 {
152- // Simulate successful Streamable HTTP response
153140 return Task . FromResult ( new HttpResponseMessage
154141 {
155142 StatusCode = HttpStatusCode . OK ,
@@ -164,6 +151,27 @@ public async Task AutoDetect_Should_Be_Default_When_UseStreamableHttp_Is_False()
164151 Assert . IsType < AutoDetectingClientTransport > ( session ) ;
165152 }
166153
154+ [ Fact ]
155+ public async Task UseStreamableHttp_True_Should_Return_StreamableHttp_Transport ( )
156+ {
157+ var options = new SseClientTransportOptions
158+ {
159+ Endpoint = new Uri ( "http://localhost:8080" ) ,
160+ UseStreamableHttp = true , // This should map to StreamableHttp mode
161+ ConnectionTimeout = TimeSpan . FromSeconds ( 2 ) ,
162+ Name = "Test Server"
163+ } ;
164+
165+ using var mockHttpHandler = new MockHttpHandler ( ) ;
166+ using var httpClient = new HttpClient ( mockHttpHandler ) ;
167+ await using var transport = new SseClientTransport ( options , httpClient , LoggerFactory ) ;
168+
169+ await using var session = await transport . ConnectAsync ( TestContext . Current . CancellationToken ) ;
170+
171+ // Should return StreamableHttpClientSessionTransport directly
172+ Assert . IsType < StreamableHttpClientSessionTransport > ( session ) ;
173+ }
174+
167175 [ Fact ]
168176 public async Task StreamableHttp_Mode_Should_Return_StreamableHttp_Transport ( )
169177 {
@@ -216,4 +224,36 @@ public async Task Sse_Mode_Should_Return_Sse_Transport()
216224 // Should return SseClientSessionTransport directly
217225 Assert . IsType < SseClientSessionTransport > ( session ) ;
218226 }
227+
228+ [ Fact ]
229+ public void GetEffectiveTransportMode_Should_Respect_TransportMode_Over_UseStreamableHttp ( )
230+ {
231+ var options = new SseClientTransportOptions
232+ {
233+ Endpoint = new Uri ( "http://localhost:8080" ) ,
234+ UseStreamableHttp = true ,
235+ TransportMode = SseTransportMode . Sse // This should override UseStreamableHttp
236+ } ;
237+
238+ var effectiveMode = options . GetEffectiveTransportMode ( ) ;
239+ Assert . Equal ( SseTransportMode . Sse , effectiveMode ) ;
240+ }
241+
242+ [ Fact ]
243+ public void GetEffectiveTransportMode_Should_Use_UseStreamableHttp_When_TransportMode_Not_Set ( )
244+ {
245+ var options1 = new SseClientTransportOptions
246+ {
247+ Endpoint = new Uri ( "http://localhost:8080" ) ,
248+ UseStreamableHttp = true
249+ } ;
250+ Assert . Equal ( SseTransportMode . StreamableHttp , options1 . GetEffectiveTransportMode ( ) ) ;
251+
252+ var options2 = new SseClientTransportOptions
253+ {
254+ Endpoint = new Uri ( "http://localhost:8080" ) ,
255+ UseStreamableHttp = false
256+ } ;
257+ Assert . Equal ( SseTransportMode . AutoDetect , options2 . GetEffectiveTransportMode ( ) ) ;
258+ }
219259}
0 commit comments