@@ -72,7 +72,7 @@ def mock_client(self, monkeypatch: pytest.MonkeyPatch) -> Mock:
7272 mock .get_log_events .return_value = {
7373 "events" : [],
7474 "nextBackwardToken" : "bwd" ,
75- "nextFormartToken " : "fwd" ,
75+ "nextForwardToken " : "fwd" ,
7676 }
7777 return mock
7878
@@ -183,6 +183,7 @@ async def test_poll_logs_non_empty_response(
183183 {"timestamp" : 1696586513234 , "message" : "SGVsbG8=" },
184184 {"timestamp" : 1696586513235 , "message" : "V29ybGQ=" },
185185 ]
186+ poll_logs_request .limit = 2
186187 job_submission_logs = log_storage .poll_logs (project , poll_logs_request )
187188
188189 assert job_submission_logs .logs == [
@@ -199,20 +200,21 @@ async def test_poll_logs_non_empty_response(
199200 ]
200201
201202 @pytest .mark .asyncio
203+ @pytest .mark .parametrize ("descending" , [False , True ])
202204 async def test_poll_logs_empty_response (
203205 self ,
204206 project : ProjectModel ,
205207 log_storage : CloudWatchLogStorage ,
206208 mock_client : Mock ,
207209 poll_logs_request : PollLogsRequest ,
210+ descending : bool ,
208211 ):
209- # Check that we don't use the workaround when descending=False -> startFromHead=True
210- # https://github.com/dstackai/dstack/issues/1647
211212 mock_client .get_log_events .return_value ["events" ] = []
213+ poll_logs_request .descending = descending
212214 job_submission_logs = log_storage .poll_logs (project , poll_logs_request )
213215
214216 assert job_submission_logs .logs == []
215- mock_client .get_log_events .assert_called_once ()
217+ assert mock_client .get_log_events .call_count == 2
216218
217219 @pytest .mark .asyncio
218220 async def test_poll_logs_descending_non_empty_response_on_first_call (
@@ -227,6 +229,7 @@ async def test_poll_logs_descending_non_empty_response_on_first_call(
227229 {"timestamp" : 1696586513235 , "message" : "V29ybGQ=" },
228230 ]
229231 poll_logs_request .descending = True
232+ poll_logs_request .limit = 2
230233 job_submission_logs = log_storage .poll_logs (project , poll_logs_request )
231234
232235 assert job_submission_logs .logs == [
@@ -243,16 +246,18 @@ async def test_poll_logs_descending_non_empty_response_on_first_call(
243246 ]
244247
245248 @pytest .mark .asyncio
246- async def test_poll_logs_descending_two_first_calls_return_empty_response (
249+ async def test_poll_logs_descending_some_responses_are_empty (
247250 self ,
248251 project : ProjectModel ,
249252 log_storage : CloudWatchLogStorage ,
250253 mock_client : Mock ,
251254 poll_logs_request : PollLogsRequest ,
252255 ):
253256 # The first two calls return empty event lists, though the token is not the same, meaning
254- # there are more events.
255- # https://github.com/dstackai/dstack/issues/1647
257+ # there are more events, see: https://github.com/dstackai/dstack/issues/1647
258+ # As the third call returns less events than requested (2 < 3), we continue to poll until
259+ # accumulate enough events (2 + 2) and return exactly the requested number of events (3),
260+ # see: https://github.com/dstackai/dstack/issues/2500
256261 mock_client .get_log_events .side_effect = [
257262 {
258263 "events" : [],
@@ -272,8 +277,22 @@ async def test_poll_logs_descending_two_first_calls_return_empty_response(
272277 "nextBackwardToken" : "bwd3" ,
273278 "nextForwardToken" : "fwd" ,
274279 },
280+ {
281+ "events" : [],
282+ "nextBackwardToken" : "bwd4" ,
283+ "nextForwardToken" : "fwd" ,
284+ },
285+ {
286+ "events" : [
287+ {"timestamp" : 1696586513232 , "message" : "aW5pdCAx" },
288+ {"timestamp" : 1696586513233 , "message" : "aW5pdCAy" },
289+ ],
290+ "nextBackwardToken" : "bwd5" ,
291+ "nextForwardToken" : "fwd" ,
292+ },
275293 ]
276294 poll_logs_request .descending = True
295+ poll_logs_request .limit = 3
277296 job_submission_logs = log_storage .poll_logs (project , poll_logs_request )
278297
279298 assert job_submission_logs .logs == [
@@ -287,8 +306,13 @@ async def test_poll_logs_descending_two_first_calls_return_empty_response(
287306 log_source = LogEventSource .STDOUT ,
288307 message = "SGVsbG8=" ,
289308 ),
309+ LogEvent (
310+ timestamp = datetime (2023 , 10 , 6 , 10 , 1 , 53 , 233000 , tzinfo = timezone .utc ),
311+ log_source = LogEventSource .STDOUT ,
312+ message = "aW5pdCAy" ,
313+ ),
290314 ]
291- assert mock_client .get_log_events .call_count == 3
315+ assert mock_client .get_log_events .call_count == 5
292316
293317 @pytest .mark .asyncio
294318 async def test_poll_logs_descending_empty_response_with_same_token (
@@ -352,7 +376,7 @@ def _response_producer(*args, **kwargs):
352376 job_submission_logs = log_storage .poll_logs (project , poll_logs_request )
353377
354378 assert job_submission_logs .logs == []
355- assert mock_client .get_log_events .call_count == 11 # initial call + 10 tries
379+ assert mock_client .get_log_events .call_count == 10
356380
357381 @pytest .mark .asyncio
358382 async def test_poll_logs_request_params_asc_no_diag_no_dates (
@@ -366,11 +390,13 @@ async def test_poll_logs_request_params_asc_no_diag_no_dates(
366390 poll_logs_request .limit = 5
367391 poll_logs_request .diagnose = False
368392 log_storage .poll_logs (project , poll_logs_request )
369- mock_client .get_log_events .assert_called_once_with (
393+ assert mock_client .get_log_events .call_count == 2
394+ mock_client .get_log_events .assert_called_with (
370395 logGroupName = "test-group" ,
371396 logStreamName = "test-proj/test-run/1b0e1b45-2f8c-4ab6-8010-a0d1a3e44e0e/job" ,
372397 limit = 5 ,
373398 startFromHead = True ,
399+ nextToken = "fwd" ,
374400 )
375401
376402 @pytest .mark .asyncio
@@ -394,13 +420,15 @@ async def test_poll_logs_request_params_desc_diag_with_dates(
394420 poll_logs_request .limit = 10
395421 poll_logs_request .diagnose = True
396422 log_storage .poll_logs (project , poll_logs_request )
397- mock_client .get_log_events .assert_called_once_with (
423+ assert mock_client .get_log_events .call_count == 2
424+ mock_client .get_log_events .assert_called_with (
398425 logGroupName = "test-group" ,
399426 logStreamName = "test-proj/test-run/1b0e1b45-2f8c-4ab6-8010-a0d1a3e44e0e/runner" ,
400427 limit = 10 ,
401428 startFromHead = False ,
402429 startTime = 1696586513235 ,
403430 endTime = 1696672913234 ,
431+ nextToken = "bwd" ,
404432 )
405433
406434 @pytest .mark .asyncio
0 commit comments