22
33namespace KLP \KlpMcpServer \Tests \Controllers ;
44
5- use Exception ;
6- use JsonException ;
75use KLP \KlpMcpServer \Controllers \StreamableHttpController ;
86use KLP \KlpMcpServer \Protocol \MCPProtocolInterface ;
97use KLP \KlpMcpServer \Server \MCPServerInterface ;
8+ use KLP \KlpMcpServer \Transports \Exception \StreamableHttpTransportException ;
109use PHPUnit \Framework \Attributes \Small ;
1110use PHPUnit \Framework \MockObject \MockObject ;
1211use PHPUnit \Framework \TestCase ;
@@ -53,7 +52,6 @@ public function test_handle_with_post_request(): void
5352 // Create a POST request with some content
5453 $ clientId = 'test-client-id ' ;
5554 $ message = ['jsonrpc ' => '2.0 ' , 'method ' => 'test ' , 'params ' => [], 'id ' => 1 ];
56- $ responseResult = [['jsonrpc ' => '2.0 ' , 'result ' => 'success ' , 'id ' => 1 ]];
5755
5856 $ request = new Request (
5957 [], // query parameters
@@ -76,17 +74,19 @@ public function test_handle_with_post_request(): void
7674 ->method ('requestMessage ' )
7775 ->with ($ clientId , $ message );
7876
79- $ this ->mockServer ->expects ($ this ->once ())
80- ->method ('getResponseResult ' )
81- ->with ($ clientId )
82- ->willReturn ($ responseResult );
83-
8477 // Call handle() which should internally call postHandle()
8578 $ response = $ this ->controller ->handle ($ request );
8679
8780 // Verify the response is what we expect from postHandle()
88- $ this ->assertInstanceOf (JsonResponse::class, $ response );
89- $ this ->assertEquals ($ responseResult [0 ], json_decode ($ response ->getContent (), true ));
81+ $ this ->assertInstanceOf (StreamedResponse::class, $ response );
82+ $ this ->assertEquals ('text/event-stream ' , $ response ->headers ->get ('Content-Type ' ));
83+ $ this ->assertEquals ('no-cache, private ' , $ response ->headers ->get ('Cache-Control ' ));
84+ $ this ->assertEquals ('no ' , $ response ->headers ->get ('X-Accel-Buffering ' ));
85+
86+ // Execute the streamed response to trigger the callback
87+ ob_start ();
88+ $ response ->sendContent ();
89+ ob_end_clean ();
9090 }
9191
9292 public function test_gethandle (): void
@@ -109,7 +109,6 @@ public function test_postHandle_with_single_message(): void
109109 $ clientId = 'test-client-id ' ;
110110 // The controller checks for 'jsonrpc' key to determine if it's a single message
111111 $ message = ['jsonrpc ' => '2.0 ' , 'method ' => 'test ' , 'params ' => [], 'id ' => 1 ];
112- $ responseResult = [['jsonrpc ' => '2.0 ' , 'result ' => 'success ' , 'id ' => 1 ]];
113112
114113 // Create request with headers and content
115114 $ request = new Request (
@@ -135,27 +134,22 @@ public function test_postHandle_with_single_message(): void
135134 $ this ->mockServer ->expects ($ this ->once ())
136135 ->method ('requestMessage ' );
137136
138- $ this ->mockServer ->expects ($ this ->once ())
139- ->method ('getResponseResult ' )
140- ->willReturn ($ responseResult );
141-
142137 // Set up logger mock expectations - we can't be too specific about the parameters
143138 $ this ->mockLogger ->expects ($ this ->atLeastOnce ())
144139 ->method ('debug ' );
145140
146141 // Call the method and verify response
147142 $ response = $ this ->controller ->postHandle ($ request );
148143
149- $ this ->assertInstanceOf (JsonResponse::class, $ response );
144+ $ this ->assertInstanceOf (StreamedResponse::class, $ response );
145+ $ this ->assertEquals ('text/event-stream ' , $ response ->headers ->get ('Content-Type ' ));
146+ $ this ->assertEquals ('no-cache, private ' , $ response ->headers ->get ('Cache-Control ' ));
147+ $ this ->assertEquals ('no ' , $ response ->headers ->get ('X-Accel-Buffering ' ));
150148
151- // Instead of checking the exact content, just verify it's a valid JSON response
152- $ content = json_decode ($ response ->getContent (), true );
153- $ this ->assertIsArray ($ content );
154- // The response might have either 'jsonrpc' or 'jsonrpc' key depending on the implementation
155- $ this ->assertTrue (
156- isset ($ content ['jsonrpc ' ]) || isset ($ content ['jsonrpc ' ]),
157- 'Response should have either jsonrpc or jsonrpc key '
158- );
149+ // Execute the streamed response to trigger the callback
150+ ob_start ();
151+ $ response ->sendContent ();
152+ ob_end_clean ();
159153 }
160154
161155 public function test_postHandle_with_multiple_messages (): void
@@ -197,7 +191,7 @@ public function test_postHandle_with_multiple_messages(): void
197191 return true ;
198192 }));
199193
200- $ this ->mockServer ->expects ($ this ->once ())
194+ $ this ->mockServer ->expects ($ this ->never ())
201195 ->method ('connect ' );
202196
203197 // Set up logger mock expectations
@@ -224,7 +218,6 @@ public function test_postHandle_with_fallback_client_id(): void
224218 {
225219 $ clientId = 'fallback-client-id ' ;
226220 $ message = ['jsonrpc ' => '2.0 ' , 'method ' => 'test ' , 'params ' => [], 'id ' => 1 ];
227- $ responseResult = [['jsonrpc ' => '2.0 ' , 'result ' => 'success ' , 'id ' => 1 ]];
228221
229222 // Create request without mcp-session-id header
230223 $ request = new Request (
@@ -250,16 +243,18 @@ public function test_postHandle_with_fallback_client_id(): void
250243 ->method ('requestMessage ' )
251244 ->with ($ clientId , $ message );
252245
253- $ this ->mockServer ->expects ($ this ->once ())
254- ->method ('getResponseResult ' )
255- ->with ($ clientId )
256- ->willReturn ($ responseResult );
257-
258246 // Call the method and verify response
259247 $ response = $ this ->controller ->postHandle ($ request );
260248
261- $ this ->assertInstanceOf (JsonResponse::class, $ response );
262- $ this ->assertEquals ($ responseResult [0 ], json_decode ($ response ->getContent (), true ));
249+ $ this ->assertInstanceOf (StreamedResponse::class, $ response );
250+ $ this ->assertEquals ('text/event-stream ' , $ response ->headers ->get ('Content-Type ' ));
251+ $ this ->assertEquals ('no-cache, private ' , $ response ->headers ->get ('Cache-Control ' ));
252+ $ this ->assertEquals ('no ' , $ response ->headers ->get ('X-Accel-Buffering ' ));
253+
254+ // Execute the streamed response to trigger the callback
255+ ob_start ();
256+ $ response ->sendContent ();
257+ ob_end_clean ();
263258 }
264259
265260 public function test_postHandle_with_json_parse_error (): void
@@ -303,30 +298,37 @@ public function test_postHandle_with_general_exception(): void
303298 );
304299 $ request ->headers ->set ('mcp-session-id ' , $ clientId );
305300
306- // Set up server mock to return a result that will trigger the exception
301+ // Set up server mock to throw an exception
307302 $ this ->mockServer ->expects ($ this ->once ())
308303 ->method ('setProtocolVersion ' )
309304 ->with (MCPProtocolInterface::PROTOCOL_VERSION_STREAMABE_HTTP );
310305
311306 $ this ->mockServer ->expects ($ this ->once ())
312307 ->method ('requestMessage ' )
313- ->with ($ clientId , $ message );
314-
315- // Return an empty array to trigger the "Result is not an array" exception
316- // The controller checks for !isset($result[0])
317- $ this ->mockServer ->expects ($ this ->once ())
318- ->method ('getResponseResult ' )
319- ->with ($ clientId )
320- ->willReturn ([]);
308+ ->with ($ clientId , $ message )
309+ ->willThrowException (new StreamableHttpTransportException ('Test exception ' ));
321310
322311 // Call the method and verify response
323312 $ response = $ this ->controller ->postHandle ($ request );
324313
325- $ this ->assertInstanceOf (JsonResponse::class, $ response );
326- $ this ->assertEquals (400 , $ response ->getStatusCode ());
327- $ this ->assertEquals (
328- ['jsonrpc ' => 2.0 , 'error ' => ['code ' => -32700 , 'message ' => 'Result is not an array ' ]],
329- json_decode ($ response ->getContent (), true )
330- );
314+ // The response is still a StreamedResponse because exceptions in callbacks are not caught
315+ $ this ->assertInstanceOf (StreamedResponse::class, $ response );
316+ $ this ->assertEquals ('text/event-stream ' , $ response ->headers ->get ('Content-Type ' ));
317+ $ this ->assertEquals ('no-cache, private ' , $ response ->headers ->get ('Cache-Control ' ));
318+ $ this ->assertEquals ('no ' , $ response ->headers ->get ('X-Accel-Buffering ' ));
319+
320+ // The exception will be thrown when the callback is executed
321+ $ this ->expectException (StreamableHttpTransportException::class);
322+ $ this ->expectExceptionMessage ('Test exception ' );
323+
324+ try {
325+ ob_start ();
326+ $ response ->sendContent ();
327+ } finally {
328+ // Always clean up the output buffer, even if an exception is thrown
329+ if (ob_get_level () > 0 ) {
330+ ob_end_clean ();
331+ }
332+ }
331333 }
332334}
0 commit comments