@@ -64,8 +64,7 @@ async def handle_call_tool(name: str, args: dict):
6464
6565
6666# Test fixtures
67- @pytest .fixture
68- async def server_app ()-> Starlette :
67+ def make_server_app ()-> Starlette :
6968 """Create test Starlette app with SSE transport"""
7069 sse = SseServerTransport ("/messages/" )
7170 server = TestServer ()
@@ -93,43 +92,46 @@ def space_around_test():
9392 yield
9493 time .sleep (0.1 )
9594
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" ))
10498 print (f'starting server on { server_port } ' )
105- proc . start ()
99+ server . run ()
106100
107101 # Give server time to start
108102 while not server .started :
109103 print ('waiting for server to start' )
110104 time .sleep (0.5 )
111105
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" )
133135
134136@pytest .fixture ()
135137async def http_client (server , server_url ) -> AsyncGenerator [httpx .AsyncClient , None ]:
0 commit comments