1111from runloop_api_client .sdk import AsyncDevbox , AsyncRunloopSDK
1212from tests .smoketests .utils import unique_name
1313from runloop_api_client .lib .polling import PollingConfig
14+ from runloop_api_client .lib .polling_async import async_poll_until
1415
1516pytestmark = [pytest .mark .smoketest , pytest .mark .asyncio ]
1617
@@ -1042,18 +1043,20 @@ class TestAsyncDevboxLogs:
10421043
10431044 @pytest .mark .timeout (THIRTY_SECOND_TIMEOUT )
10441045 async def test_logs_basic (self , shared_devbox : AsyncDevbox ) -> None :
1045- """Test retrieving devbox logs returns valid response structure ."""
1046+ """Test retrieving unfiltered devbox logs."""
10461047 test_message = "async basic log test message"
10471048 result = await shared_devbox .cmd .exec (f'echo "{ test_message } "' )
10481049 assert result .exit_code == 0
10491050
1050- logs = await shared_devbox .logs ()
1051-
1052- assert logs is not None
1053- assert hasattr (logs , "logs" )
1054- assert isinstance (logs .logs , list )
1055- log_content = " " .join (str (log ) for log in logs .logs )
1056- assert test_message in log_content
1051+ # Log ingestion is async — logs may not be queryable immediately after
1052+ # command execution. Poll every 1s with a 10s timeout (well within the
1053+ # 30s test timeout) to accommodate variable ingestion latency.
1054+ logs = await async_poll_until (
1055+ retriever = lambda : shared_devbox .logs (),
1056+ is_terminal = lambda l : any (test_message in (log .message or "" ) for log in l .logs ),
1057+ config = PollingConfig (timeout_seconds = 10 , interval_seconds = 1 ),
1058+ )
1059+ assert any (test_message in (log .message or "" ) for log in logs .logs )
10571060
10581061 @pytest .mark .timeout (THIRTY_SECOND_TIMEOUT )
10591062 async def test_logs_with_execution_filter (self , shared_devbox : AsyncDevbox ) -> None :
@@ -1062,13 +1065,12 @@ async def test_logs_with_execution_filter(self, shared_devbox: AsyncDevbox) -> N
10621065 result = await shared_devbox .cmd .exec (f'echo "{ test_message } "' )
10631066 assert result .exit_code == 0
10641067
1065- logs = await shared_devbox .logs (execution_id = result .execution_id )
1066-
1067- assert logs is not None
1068- assert hasattr (logs , "logs" )
1069- assert isinstance (logs .logs , list )
1070- log_content = " " .join (str (log ) for log in logs .logs )
1071- assert test_message in log_content
1068+ logs = await async_poll_until (
1069+ retriever = lambda : shared_devbox .logs (execution_id = result .execution_id ),
1070+ is_terminal = lambda l : any (test_message in (log .message or "" ) for log in l .logs ),
1071+ config = PollingConfig (timeout_seconds = 10 , interval_seconds = 1 ),
1072+ )
1073+ assert any (test_message in (log .message or "" ) for log in logs .logs )
10721074
10731075 @pytest .mark .timeout (THIRTY_SECOND_TIMEOUT )
10741076 async def test_logs_with_shell_name_filter (self , shared_devbox : AsyncDevbox ) -> None :
@@ -1080,10 +1082,9 @@ async def test_logs_with_shell_name_filter(self, shared_devbox: AsyncDevbox) ->
10801082 result = await shell .exec (f'echo "{ test_message } "' )
10811083 assert result .exit_code == 0
10821084
1083- logs = await shared_devbox .logs (shell_name = shell_name )
1084-
1085- assert logs is not None
1086- assert hasattr (logs , "logs" )
1087- assert isinstance (logs .logs , list )
1088- log_content = " " .join (str (log ) for log in logs .logs )
1089- assert test_message in log_content
1085+ logs = await async_poll_until (
1086+ retriever = lambda : shared_devbox .logs (shell_name = shell_name ),
1087+ is_terminal = lambda l : any (test_message in (log .message or "" ) for log in l .logs ),
1088+ config = PollingConfig (timeout_seconds = 10 , interval_seconds = 1 ),
1089+ )
1090+ assert any (test_message in (log .message or "" ) for log in logs .logs )
0 commit comments