Skip to content
Closed
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
48 changes: 47 additions & 1 deletion tests/integration/test_actor.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@
from typing import TYPE_CHECKING

from ._utils import get_random_resource_name, maybe_await
from apify_client._models import Actor, Build, ListOfActors, Run
from apify_client._models import (
Actor,
Build,
ListOfActors,
PayPerEventActorPricingInfo,
PricePerDatasetItemActorPricingInfo,
Run,
)
from apify_client._resource_clients import BuildClient, BuildClientAsync

if TYPE_CHECKING:
Expand Down Expand Up @@ -172,3 +179,42 @@ async def test_actor_validate_input(client: ApifyClient | ApifyClientAsync) -> N
# Valid input (hello-world accepts empty input or simple input)
is_valid = await maybe_await(actor_client.validate_input({}))
assert is_valid is True


async def test_get_actor_with_tiered_pricing(client: ApifyClient | ApifyClientAsync) -> None:
"""Regression test for apify/apify-client-python#811.

`apify/facebook-pages-scraper` historically returns `pricingInfos` entries that use tiered
pricing — both `PRICE_PER_DATASET_ITEM` with `tieredPricing` (instead of `pricePerUnitUsd`)
and `PAY_PER_EVENT` with `eventTieredPricingUsd` (instead of `eventPriceUsd`). Earlier
versions of the Pydantic models required the flat-price fields and rejected the response.
"""
actor = await maybe_await(client.actor('apify/facebook-pages-scraper').get())
assert isinstance(actor, Actor)
assert actor.pricing_infos is not None

tiered_ppr = [
pi
for pi in actor.pricing_infos
if isinstance(pi, PricePerDatasetItemActorPricingInfo) and pi.tiered_pricing is not None
]
assert tiered_ppr, 'expected at least one PRICE_PER_DATASET_ITEM entry with tiered_pricing'
tiered_pricing = tiered_ppr[0].tiered_pricing
assert tiered_pricing is not None
tiered_entry = next(iter(tiered_pricing.values()))
assert tiered_entry.tiered_price_per_unit_usd >= 0

tiered_ppe_events = [
event
for pi in actor.pricing_infos
if isinstance(pi, PayPerEventActorPricingInfo)
and pi.pricing_per_event is not None
and pi.pricing_per_event.actor_charge_events is not None
for event in pi.pricing_per_event.actor_charge_events.values()
if event.event_tiered_pricing_usd is not None
]
assert tiered_ppe_events, 'expected at least one PAY_PER_EVENT charge event with event_tiered_pricing_usd'
event_tiered_pricing = tiered_ppe_events[0].event_tiered_pricing_usd
assert event_tiered_pricing is not None
tiered_event_entry = next(iter(event_tiered_pricing.values()))
assert tiered_event_entry.tiered_event_price_usd >= 0
22 changes: 22 additions & 0 deletions tests/integration/test_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from ._utils import get_random_resource_name, maybe_await
from apify_client._models import Actor, Build, ListOfBuilds
from apify_client._resource_clients import BuildClient, BuildClientAsync

if TYPE_CHECKING:
from apify_client import ApifyClient, ApifyClientAsync
Expand Down Expand Up @@ -188,6 +189,27 @@ async def test_build_delete_and_abort(client: ApifyClient | ApifyClientAsync) ->
await maybe_await(actor_client.delete())


async def test_default_build_with_semver_version(client: ApifyClient | ApifyClientAsync) -> None:
"""Regression test for apify/apify-client-python#811.

`apify/facebook-pages-scraper` declares its actor version as `0.0.1`, a three-segment
SemVer string. The earlier OpenAPI pattern `^[0-9]+\\.[0-9]+$` rejected anything beyond
two segments, so simply fetching the default build raised a Pydantic validation error.
"""
actor = client.actor('apify/facebook-pages-scraper')
build_client = await maybe_await(actor.default_build())
assert isinstance(build_client, BuildClient | BuildClientAsync)
build = await maybe_await(build_client.get())
assert isinstance(build, Build)
assert build.actor_definition is not None
assert build.actor_definition.version is not None
# The fix must accept any multi-segment dot-separated numeric version
# (e.g. "0.1", "1.0", "0.0.1").
segments = build.actor_definition.version.split('.')
assert len(segments) >= 2
assert all(seg.isdigit() for seg in segments)


async def test_build_get_open_api_definition(client: ApifyClient | ApifyClientAsync) -> None:
"""Test getting OpenAPI definition for a build."""
# Get builds for hello-world actor
Expand Down
Loading