Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions src/apify/_charging.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,9 @@ class ChargingManagerImplementation(ChargingManager):
LOCAL_CHARGING_LOG_DATASET_NAME = 'charging-log'

def __init__(self, configuration: Configuration, client: ApifyClientAsync) -> None:
self._max_total_charge_usd = configuration.max_total_charge_usd or Decimal('inf')
self._max_total_charge_usd = (
configuration.max_total_charge_usd if configuration.max_total_charge_usd is not None else Decimal('inf')
)
self._configuration = configuration
self._is_at_home = configuration.is_at_home
self._actor_run_id = configuration.actor_run_id
Expand Down Expand Up @@ -413,7 +415,11 @@ async def _fetch_pricing_info(self) -> _FetchedPricingInfoDict:
return _FetchedPricingInfoDict(
pricing_info=self._configuration.actor_pricing_info,
charged_event_counts=self._configuration.charged_event_counts,
max_total_charge_usd=self._configuration.max_total_charge_usd or Decimal('inf'),
max_total_charge_usd=(
self._configuration.max_total_charge_usd
if self._configuration.max_total_charge_usd is not None
else Decimal('inf')
),
)

# Fall back to API call
Expand All @@ -436,7 +442,11 @@ async def _fetch_pricing_info(self) -> _FetchedPricingInfoDict:
return _FetchedPricingInfoDict(
pricing_info=None,
charged_event_counts={},
max_total_charge_usd=self._configuration.max_total_charge_usd or Decimal('inf'),
max_total_charge_usd=(
self._configuration.max_total_charge_usd
if self._configuration.max_total_charge_usd is not None
else Decimal('inf')
),
)

def _get_event_price(self, event_name: str) -> Decimal:
Expand Down
30 changes: 30 additions & 0 deletions tests/unit/actor/test_charging_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,36 @@ async def test_get_max_total_charge_usd(mock_client: MagicMock) -> None:
assert cm.get_max_total_charge_usd() == Decimal('42.50')


async def test_max_total_charge_usd_zero_blocks_charging(mock_client: MagicMock) -> None:
"""Test max_total_charge_usd=0 is preserved and blocks all charging (not silently converted to inf)."""
pricing_info = _make_ppe_pricing_info({'search': Decimal('0.01')})
config = _make_config(
test_pay_per_event=True,
actor_pricing_info=pricing_info,
charged_event_counts={},
max_total_charge_usd=Decimal(0),
)
cm = ChargingManagerImplementation(config, mock_client)
async with cm:
assert cm.get_max_total_charge_usd() == Decimal(0)
assert cm.is_event_charge_limit_reached('search') is True
result = await cm.charge('search', count=5)
assert result.charged_count == 0
assert result.event_charge_limit_reached is True


async def test_max_total_charge_usd_zero_preserved_without_pricing(mock_client: MagicMock) -> None:
"""Test max_total_charge_usd=0 is preserved through the no-pricing fallback path."""
config = _make_config(
max_total_charge_usd=Decimal(0),
actor_pricing_info=None,
charged_event_counts={},
)
cm = ChargingManagerImplementation(config, mock_client)
async with cm:
assert cm.get_max_total_charge_usd() == Decimal(0)


async def test_compute_push_data_limit_no_ppe(mock_client: MagicMock) -> None:
"""Returns items_count when no PPE pricing is configured (prices are zero)."""
config = _make_config(actor_pricing_info=None, charged_event_counts={})
Expand Down
Loading