@@ -279,6 +279,145 @@ def test_execute_python_script(self, server, daemon_process):
279279 assert "Python" in result .stdout
280280
281281
282+ class TestGetDaemon :
283+ """Test get_daemon functionality with real daemons"""
284+
285+ def test_get_existing_daemon (self , server , daemon_process ):
286+ """Test get_daemon returns DaemonInfo for connected daemon"""
287+ daemon_id , _ = daemon_process
288+
289+ daemon = server .get_daemon (daemon_id )
290+
291+ assert daemon is not None
292+ assert daemon .id == daemon_id
293+ assert isinstance (daemon .version , str )
294+ assert isinstance (daemon .labels , dict )
295+ assert isinstance (daemon .is_busy , bool )
296+
297+ def test_get_nonexistent_daemon (self , server , daemon_process ):
298+ """Test get_daemon returns None for non-existent daemon"""
299+ _ , _ = daemon_process
300+
301+ result = server .get_daemon ("definitely-not-a-real-daemon-id-12345" )
302+ assert result is None
303+
304+ def test_get_daemon_with_labels (self , server , sandd_binary ):
305+ """Test get_daemon returns daemon with labels"""
306+ daemon_id = f"test-labeled-daemon-{ os .getpid ()} "
307+ server_url = f"ws://127.0.0.1:{ server .address .split (':' )[1 ]} /ws"
308+
309+ proc = subprocess .Popen (
310+ [
311+ sandd_binary ,
312+ "--server-url" , server_url ,
313+ "--daemon-id" , daemon_id ,
314+ "--label" , "env=staging" ,
315+ "--label" , "team=backend" ,
316+ ],
317+ stdout = subprocess .DEVNULL ,
318+ stderr = subprocess .DEVNULL ,
319+ )
320+
321+ try :
322+ assert server .wait_for_daemon (daemon_id , timeout = 5.0 )
323+
324+ daemon = server .get_daemon (daemon_id )
325+ assert daemon is not None
326+ assert daemon .id == daemon_id
327+ assert daemon .labels == {"env" : "staging" , "team" : "backend" }
328+
329+ finally :
330+ proc .kill ()
331+
332+ def test_get_daemon_after_disconnect (self , server , sandd_binary ):
333+ """Test get_daemon returns None after daemon disconnects"""
334+ daemon_id = f"test-disconnect-daemon-{ os .getpid ()} "
335+ server_url = f"ws://127.0.0.1:{ server .address .split (':' )[1 ]} /ws"
336+
337+ proc = subprocess .Popen (
338+ [sandd_binary , "--server-url" , server_url , "--daemon-id" , daemon_id ],
339+ stdout = subprocess .DEVNULL ,
340+ stderr = subprocess .DEVNULL ,
341+ )
342+
343+ try :
344+ # Wait for connection
345+ assert server .wait_for_daemon (daemon_id , timeout = 5.0 )
346+
347+ # Verify daemon is there
348+ daemon = server .get_daemon (daemon_id )
349+ assert daemon is not None
350+ assert daemon .id == daemon_id
351+
352+ # Kill the daemon
353+ proc .kill ()
354+ proc .wait ()
355+
356+ # Give some time for disconnect to register
357+ time .sleep (0.5 )
358+
359+ # Daemon should no longer be found
360+ daemon = server .get_daemon (daemon_id )
361+ assert daemon is None
362+ finally :
363+ try :
364+ proc .kill ()
365+ except : # noqa: E722
366+ pass
367+
368+ def test_get_daemon_multiple_times (self , server , daemon_process ):
369+ """Test calling get_daemon multiple times returns consistent results"""
370+ daemon_id , _ = daemon_process
371+
372+ # Call multiple times
373+ daemon1 = server .get_daemon (daemon_id )
374+ daemon2 = server .get_daemon (daemon_id )
375+ daemon3 = server .get_daemon (daemon_id )
376+
377+ assert daemon1 is not None
378+ assert daemon2 is not None
379+ assert daemon3 is not None
380+
381+ # All should have the same ID
382+ assert daemon1 .id == daemon2 .id == daemon3 .id == daemon_id
383+
384+ def test_get_daemon_busy_state (self , server , daemon_process ):
385+ """Test get_daemon reflects busy state"""
386+ daemon_id , _ = daemon_process
387+
388+ # Check initial state (should not be busy)
389+ daemon = server .get_daemon (daemon_id )
390+ assert daemon is not None
391+ assert daemon .is_busy is False
392+
393+ # Start a long-running command in background
394+ import threading
395+
396+ def run_long_command ():
397+ try :
398+ server .exec (daemon_id , "sleep 2" , timeout = 5 )
399+ except : # noqa: E722
400+ pass
401+
402+ thread = threading .Thread (target = run_long_command )
403+ thread .start ()
404+
405+ # Give command time to start
406+ time .sleep (0.2 )
407+
408+ # Check if daemon is now busy (might be, depending on timing)
409+ daemon_during = server .get_daemon (daemon_id )
410+ assert daemon_during is not None
411+ assert daemon_during .is_busy is True
412+
413+ # Wait for command to complete
414+ thread .join ()
415+
416+ # Daemon should not be busy anymore
417+ daemon_after = server .get_daemon (daemon_id )
418+ assert daemon_after is not None
419+ assert daemon_after .is_busy is False
420+
282421class TestServerStats :
283422 """Test server statistics with real connections"""
284423
@@ -400,7 +539,12 @@ def wait_thread():
400539 assert result_holder ["connected" ] is True
401540
402541 finally :
403- proc .kill ()
542+ proc .terminate ()
543+ try :
544+ proc .wait (timeout = 2 )
545+ except subprocess .TimeoutExpired :
546+ proc .kill ()
547+ proc .wait ()
404548
405549
406550@pytest .mark .skipif (
0 commit comments