(t *testing.T)
| 289 | } |
| 290 | |
| 291 | func TestStreamableServerShutdown(t *testing.T) { |
| 292 | ctx := context.Background() |
| 293 | |
| 294 | // This test checks that closing the streamable HTTP server actually results |
| 295 | // in client session termination, provided one of following holds: |
| 296 | // 1. The server is stateful, and therefore the hanging GET fails the connection. |
| 297 | // 2. The server is stateless, and the client uses a KeepAlive. |
| 298 | tests := []struct { |
| 299 | name string |
| 300 | stateless, keepalive bool |
| 301 | }{ |
| 302 | {"stateful", false, false}, |
| 303 | {"stateless with keepalive", true, true}, |
| 304 | } |
| 305 | |
| 306 | for _, test := range tests { |
| 307 | t.Run(test.name, func(t *testing.T) { |
| 308 | server := NewServer(testImpl, nil) |
| 309 | // Add a tool, just so we can check things are working. |
| 310 | AddTool(server, &Tool{Name: "greet"}, sayHi) |
| 311 | |
| 312 | handler := NewStreamableHTTPHandler( |
| 313 | func(req *http.Request) *Server { return server }, |
| 314 | &StreamableHTTPOptions{Stateless: test.stateless}) |
| 315 | |
| 316 | // When we shut down the server, we need to explicitly close ongoing |
| 317 | // connections. Otherwise, the hanging GET may never terminate. |
| 318 | httpServer := httptest.NewUnstartedServer(handler) |
| 319 | httpServer.Config.RegisterOnShutdown(func() { |
| 320 | for session := range server.Sessions() { |
| 321 | session.Close() |
| 322 | } |
| 323 | }) |
| 324 | httpServer.Start() |
| 325 | defer httpServer.Close() |
| 326 | |
| 327 | // Connect and run a tool. |
| 328 | var opts ClientOptions |
| 329 | if test.keepalive { |
| 330 | opts.KeepAlive = 50 * time.Millisecond |
| 331 | } |
| 332 | client := NewClient(testImpl, &opts) |
| 333 | clientSession, err := client.Connect(ctx, &StreamableClientTransport{ |
| 334 | Endpoint: httpServer.URL, |
| 335 | MaxRetries: -1, // avoid slow tests during exponential retries |
| 336 | }, nil) |
| 337 | if err != nil { |
| 338 | t.Fatal(err) |
| 339 | } |
| 340 | defer clientSession.Close() |
| 341 | |
| 342 | params := &CallToolParams{ |
| 343 | Name: "greet", |
| 344 | Arguments: map[string]any{"Name": "foo"}, |
| 345 | } |
| 346 | // Verify that we can call a tool. |
| 347 | if _, err := clientSession.CallTool(ctx, params); err != nil { |
| 348 | t.Fatalf("CallTool() failed: %v", err) |
nothing calls this directly
no test coverage detected
searching dependent graphs…