1
+ use axum:: Router ;
1
2
use rmcp:: {
2
3
ServiceExt ,
3
- transport:: { ConfigureCommandExt , SseServer , TokioChildProcess } ,
4
+ transport:: { ConfigureCommandExt , SseServer , TokioChildProcess , sse_server :: SseServerConfig } ,
4
5
} ;
6
+ use tokio:: time:: timeout;
7
+ use tokio_util:: sync:: CancellationToken ;
5
8
use tracing_subscriber:: { layer:: SubscriberExt , util:: SubscriberInitExt } ;
6
9
mod common;
7
10
use common:: calculator:: Calculator ;
8
11
9
- const BIND_ADDRESS : & str = "127.0.0.1:8000" ;
10
-
11
- #[ tokio:: test]
12
- async fn test_with_python_client ( ) -> anyhow:: Result < ( ) > {
12
+ async fn init ( ) -> anyhow:: Result < ( ) > {
13
13
let _ = tracing_subscriber:: registry ( )
14
14
. with (
15
15
tracing_subscriber:: EnvFilter :: try_from_default_env ( )
@@ -23,6 +23,14 @@ async fn test_with_python_client() -> anyhow::Result<()> {
23
23
. spawn ( ) ?
24
24
. wait ( )
25
25
. await ?;
26
+ Ok ( ( ) )
27
+ }
28
+
29
+ #[ tokio:: test]
30
+ async fn test_with_python_client ( ) -> anyhow:: Result < ( ) > {
31
+ init ( ) . await ?;
32
+
33
+ const BIND_ADDRESS : & str = "127.0.0.1:8000" ;
26
34
27
35
let ct = SseServer :: serve ( BIND_ADDRESS . parse ( ) ?)
28
36
. await ?
@@ -31,6 +39,7 @@ async fn test_with_python_client() -> anyhow::Result<()> {
31
39
let status = tokio:: process:: Command :: new ( "uv" )
32
40
. arg ( "run" )
33
41
. arg ( "tests/test_with_python/client.py" )
42
+ . arg ( format ! ( "http://{BIND_ADDRESS}/sse" ) )
34
43
. spawn ( ) ?
35
44
. wait ( )
36
45
. await ?;
@@ -39,21 +48,61 @@ async fn test_with_python_client() -> anyhow::Result<()> {
39
48
Ok ( ( ) )
40
49
}
41
50
51
+ /// Test the SSE server in a nested Axum router.
52
+ #[ tokio:: test]
53
+ async fn test_nested_with_python_client ( ) -> anyhow:: Result < ( ) > {
54
+ init ( ) . await ?;
55
+
56
+ const BIND_ADDRESS : & str = "127.0.0.1:8001" ;
57
+
58
+ // Create an SSE router
59
+ let sse_config = SseServerConfig {
60
+ bind : BIND_ADDRESS . parse ( ) ?,
61
+ sse_path : "/sse" . to_string ( ) ,
62
+ post_path : "/message" . to_string ( ) ,
63
+ ct : CancellationToken :: new ( ) ,
64
+ sse_keep_alive : None ,
65
+ } ;
66
+
67
+ let listener = tokio:: net:: TcpListener :: bind ( & sse_config. bind ) . await ?;
68
+
69
+ let ( sse_server, sse_router) = SseServer :: new ( sse_config) ;
70
+ let ct = sse_server. with_service ( Calculator :: default) ;
71
+
72
+ let main_router = Router :: new ( ) . nest ( "/nested" , sse_router) ;
73
+
74
+ let server_ct = ct. clone ( ) ;
75
+ let server = axum:: serve ( listener, main_router) . with_graceful_shutdown ( async move {
76
+ server_ct. cancelled ( ) . await ;
77
+ tracing:: info!( "sse server cancelled" ) ;
78
+ } ) ;
79
+
80
+ tokio:: spawn ( async move {
81
+ let _ = server. await ;
82
+ tracing:: info!( "sse server shutting down" ) ;
83
+ } ) ;
84
+
85
+ // Spawn the process with timeout, as failure to access the '/message' URL
86
+ // causes the client to never exit.
87
+ let status = timeout (
88
+ tokio:: time:: Duration :: from_secs ( 5 ) ,
89
+ tokio:: process:: Command :: new ( "uv" )
90
+ . arg ( "run" )
91
+ . arg ( "tests/test_with_python/client.py" )
92
+ . arg ( format ! ( "http://{BIND_ADDRESS}/nested/sse" ) )
93
+ . spawn ( ) ?
94
+ . wait ( ) ,
95
+ )
96
+ . await ?;
97
+ assert ! ( status?. success( ) ) ;
98
+ ct. cancel ( ) ;
99
+ Ok ( ( ) )
100
+ }
101
+
42
102
#[ tokio:: test]
43
103
async fn test_with_python_server ( ) -> anyhow:: Result < ( ) > {
44
- let _ = tracing_subscriber:: registry ( )
45
- . with (
46
- tracing_subscriber:: EnvFilter :: try_from_default_env ( )
47
- . unwrap_or_else ( |_| "debug" . to_string ( ) . into ( ) ) ,
48
- )
49
- . with ( tracing_subscriber:: fmt:: layer ( ) )
50
- . try_init ( ) ;
51
- tokio:: process:: Command :: new ( "uv" )
52
- . args ( [ "pip" , "install" , "-r" , "pyproject.toml" ] )
53
- . current_dir ( "tests/test_with_python" )
54
- . spawn ( ) ?
55
- . wait ( )
56
- . await ?;
104
+ init ( ) . await ?;
105
+
57
106
let transport = TokioChildProcess :: new ( tokio:: process:: Command :: new ( "uv" ) . configure ( |cmd| {
58
107
cmd. arg ( "run" ) . arg ( "tests/test_with_python/server.py" ) ;
59
108
} ) ) ?;
0 commit comments