@@ -64,8 +64,7 @@ async def handle_call_tool(name: str, args: dict):
64
64
65
65
66
66
# Test fixtures
67
- @pytest .fixture
68
- async def server_app ()-> Starlette :
67
+ def make_server_app ()-> Starlette :
69
68
"""Create test Starlette app with SSE transport"""
70
69
sse = SseServerTransport ("/messages/" )
71
70
server = TestServer ()
@@ -93,43 +92,46 @@ def space_around_test():
93
92
yield
94
93
time .sleep (0.1 )
95
94
96
- @pytest .fixture ()
97
- def server (server_app : Starlette , server_port : int ):
98
- proc = multiprocessing .Process (target = uvicorn .run , daemon = True , kwargs = {
99
- "app" : server_app ,
100
- "host" : "127.0.0.1" ,
101
- "port" : server_port ,
102
- "log_level" : "error"
103
- })
95
+ def run_server (server_port : int ):
96
+ app = make_server_app ()
97
+ server = uvicorn .Server (config = uvicorn .Config (app = app , host = "127.0.0.1" , port = server_port , log_level = "error" ))
104
98
print (f'starting server on { server_port } ' )
105
- proc . start ()
99
+ server . run ()
106
100
107
101
# Give server time to start
108
102
while not server .started :
109
103
print ('waiting for server to start' )
110
104
time .sleep (0.5 )
111
105
112
- try :
113
- yield
114
- finally :
115
- print ('killing server' )
116
- # Signal the server to stop
117
- server .should_exit = True
118
-
119
- # Force close the server's main socket
120
- if hasattr (server .servers , "servers" ):
121
- for s in server .servers :
122
- print (f'closing { s } ' )
123
- s .close ()
124
-
125
- # Wait for thread to finish
126
- proc .terminate ()
127
- proc .join (timeout = 2 )
128
- if proc .is_alive ():
129
- print ("Warning: Server thread did not exit cleanly" )
130
- # Optionally, you could add more aggressive cleanup here
131
- import _thread
132
- _thread .interrupt_main ()
106
+ @pytest .fixture ()
107
+ def server (server_port : int ):
108
+ proc = multiprocessing .Process (target = run_server , kwargs = {"server_port" : server_port }, daemon = True )
109
+ print ('starting process' )
110
+ proc .start ()
111
+
112
+ # Wait for server to be running
113
+ max_attempts = 20
114
+ attempt = 0
115
+ print ('waiting for server to start' )
116
+ while attempt < max_attempts :
117
+ try :
118
+ with socket .socket (socket .AF_INET , socket .SOCK_STREAM ) as s :
119
+ s .connect (('127.0.0.1' , server_port ))
120
+ break
121
+ except ConnectionRefusedError :
122
+ time .sleep (0.1 )
123
+ attempt += 1
124
+ else :
125
+ raise RuntimeError ("Server failed to start after {} attempts" .format (max_attempts ))
126
+
127
+ yield
128
+
129
+ print ('killing server' )
130
+ # Signal the server to stop
131
+ proc .kill ()
132
+ proc .join (timeout = 2 )
133
+ if proc .is_alive ():
134
+ print ("server process failed to terminate" )
133
135
134
136
@pytest .fixture ()
135
137
async def http_client (server , server_url ) -> AsyncGenerator [httpx .AsyncClient , None ]:
0 commit comments