Skip to content

Commit a293601

Browse files
vdusekclaude
andauthored
test: Resolve flaky webhook integration test (#640)
## Summary - The `_get_finished_run_id` helper in webhook tests asserted that at least one SUCCEEDED run of `apify/hello-world` already exists, but this is not guaranteed (runs can be cleaned up between test runs) - Instead of failing with an assertion error, the helper now falls back to starting a new hello-world actor run via `.call()` (which waits for completion) when no completed runs are found - Fixes the flaky `test_webhook_test[async]` failure seen in CI: https://github.com/apify/apify-client-python/actions/runs/22345359592/job/64658461897?pr=639 Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 638ad1d commit a293601

File tree

1 file changed

+21
-27
lines changed

1 file changed

+21
-27
lines changed

tests/integration/test_webhook.py

Lines changed: 21 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@
77

88
from __future__ import annotations
99

10-
from typing import TYPE_CHECKING, cast
10+
from typing import TYPE_CHECKING
1111

1212
if TYPE_CHECKING:
1313
from apify_client import ApifyClient, ApifyClientAsync
14-
from apify_client._models import ListOfWebhookDispatches, ListOfWebhooks, Run, Webhook, WebhookDispatch
1514

1615

1716
from ._utils import maybe_await
@@ -24,19 +23,24 @@ async def _get_finished_run_id(client: ApifyClient | ApifyClientAsync) -> str:
2423
"""Get the ID of an already-completed run of the hello-world actor.
2524
2625
Using a finished run's ID for webhook conditions ensures the webhook will never actually fire,
27-
since a completed run won't emit new events.
26+
since a completed run won't emit new events. If no completed runs exist, starts a new run and
27+
waits for it to finish.
2828
"""
2929
runs_page = await maybe_await(client.actor(HELLO_WORLD_ACTOR).runs().list(limit=1, status=ActorJobStatus.SUCCEEDED))
3030
assert runs_page is not None
31-
assert len(runs_page.items) > 0, 'No completed runs found for hello-world actor'
32-
run = cast('Run', runs_page.items[0])
31+
32+
if len(runs_page.items) > 0:
33+
return runs_page.items[0].id
34+
35+
# No completed runs found - start one and wait for it to finish
36+
run = await maybe_await(client.actor(HELLO_WORLD_ACTOR).call())
37+
assert run is not None
3338
return run.id
3439

3540

3641
async def test_list_webhooks(client: ApifyClient | ApifyClientAsync) -> None:
3742
"""Test listing webhooks."""
38-
result = await maybe_await(client.webhooks().list(limit=10))
39-
webhooks_page = cast('ListOfWebhooks', result)
43+
webhooks_page = await maybe_await(client.webhooks().list(limit=10))
4044

4145
assert webhooks_page is not None
4246
assert webhooks_page.items is not None
@@ -46,8 +50,7 @@ async def test_list_webhooks(client: ApifyClient | ApifyClientAsync) -> None:
4650

4751
async def test_list_webhooks_pagination(client: ApifyClient | ApifyClientAsync) -> None:
4852
"""Test listing webhooks with pagination."""
49-
result = await maybe_await(client.webhooks().list(limit=5, offset=0))
50-
webhooks_page = cast('ListOfWebhooks', result)
53+
webhooks_page = await maybe_await(client.webhooks().list(limit=5, offset=0))
5154

5255
assert webhooks_page is not None
5356
assert webhooks_page.items is not None
@@ -59,24 +62,22 @@ async def test_webhook_create_and_get(client: ApifyClient | ApifyClientAsync) ->
5962
run_id = await _get_finished_run_id(client)
6063

6164
# Create webhook bound to a finished run (will never fire)
62-
result = await maybe_await(
65+
created_webhook = await maybe_await(
6366
client.webhooks().create(
6467
event_types=[WebhookEventType.ACTOR_RUN_SUCCEEDED],
6568
request_url='https://httpbin.org/post',
6669
actor_run_id=run_id,
6770
is_ad_hoc=True,
6871
)
6972
)
70-
created_webhook = cast('Webhook', result)
7173
webhook_client = client.webhook(created_webhook.id)
7274

7375
try:
7476
assert created_webhook is not None
7577
assert created_webhook.id is not None
7678

7779
# Get the same webhook
78-
result = await maybe_await(webhook_client.get())
79-
retrieved_webhook = cast('Webhook', result)
80+
retrieved_webhook = await maybe_await(webhook_client.get())
8081
assert retrieved_webhook is not None
8182
assert retrieved_webhook.id == created_webhook.id
8283
finally:
@@ -88,26 +89,24 @@ async def test_webhook_update(client: ApifyClient | ApifyClientAsync) -> None:
8889
run_id = await _get_finished_run_id(client)
8990

9091
# Create webhook bound to a finished run
91-
result = await maybe_await(
92+
created_webhook = await maybe_await(
9293
client.webhooks().create(
9394
event_types=[WebhookEventType.ACTOR_RUN_SUCCEEDED],
9495
request_url='https://httpbin.org/post',
9596
actor_run_id=run_id,
9697
is_ad_hoc=True,
9798
)
9899
)
99-
created_webhook = cast('Webhook', result)
100100
webhook_client = client.webhook(created_webhook.id)
101101

102102
try:
103103
# Update webhook
104-
result = await maybe_await(
104+
updated_webhook = await maybe_await(
105105
webhook_client.update(
106106
request_url='https://httpbin.org/anything',
107107
actor_run_id=run_id,
108108
)
109109
)
110-
updated_webhook = cast('Webhook', result)
111110
assert str(updated_webhook.request_url) == 'https://httpbin.org/anything'
112111
finally:
113112
await maybe_await(webhook_client.delete())
@@ -118,21 +117,19 @@ async def test_webhook_test(client: ApifyClient | ApifyClientAsync) -> None:
118117
run_id = await _get_finished_run_id(client)
119118

120119
# Create webhook bound to a finished run
121-
result = await maybe_await(
120+
created_webhook = await maybe_await(
122121
client.webhooks().create(
123122
event_types=[WebhookEventType.ACTOR_RUN_SUCCEEDED],
124123
request_url='https://httpbin.org/post',
125124
actor_run_id=run_id,
126125
is_ad_hoc=True,
127126
)
128127
)
129-
created_webhook = cast('Webhook', result)
130128
webhook_client = client.webhook(created_webhook.id)
131129

132130
try:
133131
# Test webhook (creates a dispatch with dummy payload)
134-
result = await maybe_await(webhook_client.test())
135-
dispatch = cast('WebhookDispatch', result)
132+
dispatch = await maybe_await(webhook_client.test())
136133
assert dispatch is not None
137134
assert dispatch.id is not None
138135
finally:
@@ -144,24 +141,22 @@ async def test_webhook_dispatches(client: ApifyClient | ApifyClientAsync) -> Non
144141
run_id = await _get_finished_run_id(client)
145142

146143
# Create webhook bound to a finished run
147-
result = await maybe_await(
144+
created_webhook = await maybe_await(
148145
client.webhooks().create(
149146
event_types=[WebhookEventType.ACTOR_RUN_SUCCEEDED],
150147
request_url='https://httpbin.org/post',
151148
actor_run_id=run_id,
152149
is_ad_hoc=True,
153150
)
154151
)
155-
created_webhook = cast('Webhook', result)
156152
webhook_client = client.webhook(created_webhook.id)
157153

158154
try:
159155
# Test webhook to create a dispatch
160156
await maybe_await(webhook_client.test())
161157

162158
# List dispatches for this webhook
163-
result = await maybe_await(webhook_client.dispatches().list())
164-
dispatches = cast('ListOfWebhookDispatches', result)
159+
dispatches = await maybe_await(webhook_client.dispatches().list())
165160
assert dispatches is not None
166161
assert dispatches.items is not None
167162
assert len(dispatches.items) > 0
@@ -174,15 +169,14 @@ async def test_webhook_delete(client: ApifyClient | ApifyClientAsync) -> None:
174169
run_id = await _get_finished_run_id(client)
175170

176171
# Create webhook bound to a finished run
177-
result = await maybe_await(
172+
created_webhook = await maybe_await(
178173
client.webhooks().create(
179174
event_types=[WebhookEventType.ACTOR_RUN_SUCCEEDED],
180175
request_url='https://httpbin.org/post',
181176
actor_run_id=run_id,
182177
is_ad_hoc=True,
183178
)
184179
)
185-
created_webhook = cast('Webhook', result)
186180
webhook_client = client.webhook(created_webhook.id)
187181

188182
# Delete webhook

0 commit comments

Comments
 (0)