diff --git a/src/apify/_actor.py b/src/apify/_actor.py index dc0fe844..6b556cf3 100644 --- a/src/apify/_actor.py +++ b/src/apify/_actor.py @@ -1398,7 +1398,7 @@ def _get_default_exit_process(self) -> bool: def _get_remaining_time(self) -> timedelta | None: """Get time remaining from the Actor timeout. Returns `None` if not on an Apify platform.""" if self.is_at_home() and self.configuration.timeout_at: - return self.configuration.timeout_at - datetime.now(tz=timezone.utc) + return max(self.configuration.timeout_at - datetime.now(tz=timezone.utc), timedelta(0)) self.log.warning( 'Using `inherit` or `RemainingTime` argument is only possible when the Actor' diff --git a/tests/unit/actor/test_actor_helpers.py b/tests/unit/actor/test_actor_helpers.py index fe77a9c9..5b15ce82 100644 --- a/tests/unit/actor/test_actor_helpers.py +++ b/tests/unit/actor/test_actor_helpers.py @@ -2,7 +2,7 @@ import asyncio import warnings -from datetime import timedelta +from datetime import datetime, timedelta, timezone from typing import TYPE_CHECKING import pytest @@ -321,3 +321,26 @@ async def test_get_remaining_time_warns_when_not_at_home(caplog: pytest.LogCaptu result = Actor._get_remaining_time() assert result is None assert any('inherit' in msg or 'RemainingTime' in msg for msg in caplog.messages) + + +async def test_get_remaining_time_clamps_negative_to_zero() -> None: + """Test that _get_remaining_time returns timedelta(0) instead of a negative value when timeout is in the past.""" + async with Actor: + Actor.configuration.is_at_home = True + Actor.configuration.timeout_at = datetime.now(tz=timezone.utc) - timedelta(minutes=5) + + result = Actor._get_remaining_time() + assert result is not None + assert result == timedelta(0) + + +async def test_get_remaining_time_returns_positive_when_timeout_in_future() -> None: + """Test that _get_remaining_time returns a positive timedelta when timeout is in the future.""" + async with Actor: + Actor.configuration.is_at_home = True + Actor.configuration.timeout_at = datetime.now(tz=timezone.utc) + timedelta(minutes=5) + + result = Actor._get_remaining_time() + assert result is not None + assert result > timedelta(0) + assert result <= timedelta(minutes=5)