@@ -838,3 +838,98 @@ func falseSchema() *jsonschema.Schema { return &jsonschema.Schema{Not: &jsonsche
838838func nopHandler (context.Context , * ServerSession , * CallToolParamsFor [map [string ]any ]) (* CallToolResult , error ) {
839839 return nil , nil
840840}
841+
842+ func TestKeepAlive (t * testing.T ) {
843+ ctx , cancel := context .WithTimeout (context .Background (), 5 * time .Second )
844+ defer cancel ()
845+
846+ ct , st := NewInMemoryTransports ()
847+
848+ serverOpts := & ServerOptions {
849+ KeepAlive : 100 * time .Millisecond ,
850+ }
851+ s := NewServer ("testServer" , "v1.0.0" , serverOpts )
852+ s .AddTools (NewServerTool ("greet" , "say hi" , sayHi ))
853+
854+ ss , err := s .Connect (ctx , st )
855+ if err != nil {
856+ t .Fatal (err )
857+ }
858+ defer ss .Close ()
859+
860+ clientOpts := & ClientOptions {
861+ KeepAlive : 100 * time .Millisecond ,
862+ }
863+ c := NewClient ("testClient" , "v1.0.0" , clientOpts )
864+ cs , err := c .Connect (ctx , ct )
865+ if err != nil {
866+ t .Fatal (err )
867+ }
868+ defer cs .Close ()
869+
870+ // Wait for a few keepalive cycles to ensure pings are working
871+ time .Sleep (300 * time .Millisecond )
872+
873+ // Test that the connection is still alive by making a call
874+ result , err := cs .CallTool (ctx , & CallToolParams {
875+ Name : "greet" ,
876+ Arguments : map [string ]any {"Name" : "user" },
877+ })
878+ if err != nil {
879+ t .Fatalf ("call failed after keepalive: %v" , err )
880+ }
881+ if len (result .Content ) == 0 {
882+ t .Fatal ("expected content in result" )
883+ }
884+ if textContent , ok := result .Content [0 ].(* TextContent ); ! ok || textContent .Text != "hi user" {
885+ t .Fatalf ("unexpected result: %v" , result .Content [0 ])
886+ }
887+ }
888+
889+ func TestKeepAliveFailure (t * testing.T ) {
890+ ctx , cancel := context .WithTimeout (context .Background (), 5 * time .Second )
891+ defer cancel ()
892+
893+ ct , st := NewInMemoryTransports ()
894+
895+ // Server without keepalive (to test one-sided keepalive)
896+ s := NewServer ("testServer" , "v1.0.0" , nil )
897+ s .AddTools (NewServerTool ("greet" , "say hi" , sayHi ))
898+ ss , err := s .Connect (ctx , st )
899+ if err != nil {
900+ t .Fatal (err )
901+ }
902+
903+ // Client with short keepalive
904+ clientOpts := & ClientOptions {
905+ KeepAlive : 50 * time .Millisecond ,
906+ }
907+ c := NewClient ("testClient" , "v1.0.0" , clientOpts )
908+ cs , err := c .Connect (ctx , ct )
909+ if err != nil {
910+ t .Fatal (err )
911+ }
912+ defer cs .Close ()
913+
914+ // Let the connection establish properly first
915+ time .Sleep (30 * time .Millisecond )
916+
917+ // simulate ping failure
918+ ss .Close ()
919+
920+ // Wait for keepalive to detect the failure and close the client
921+ // check periodically instead of just waiting
922+ deadline := time .Now ().Add (1 * time .Second )
923+ for time .Now ().Before (deadline ) {
924+ _ , err = cs .CallTool (ctx , & CallToolParams {
925+ Name : "greet" ,
926+ Arguments : map [string ]any {"Name" : "user" },
927+ })
928+ if errors .Is (err , ErrConnectionClosed ) {
929+ return // Test passed
930+ }
931+ time .Sleep (25 * time .Millisecond )
932+ }
933+
934+ t .Errorf ("expected connection to be closed by keepalive, but it wasn't. Last error: %v" , err )
935+ }
0 commit comments