Skip to content

Commit 252eb4e

Browse files
vdusekclaude
andauthored
test: Increase total test coverage and reduce E2E tests (#783)
## Summary - Reduce expensive e2e tests from 65 to 37 (-43%) by migrating storage/SDK-logic tests to integration and unit tiers. - Add new unit tests covering previously untested modules and code paths. - Extend existing unit tests for `ApifyEventManager`, `Configuration`, crypto utilities, and more. - Test coverage increased from 81.86% to 85.49%. - Total execution time is reduced roughly by ~50%. - Removed duplicate `test_actor_handles_migrating_event_correctly` from `test_actor_lifecycle.py` - same behavior is already tested in `test_apify_event_manager.py::test_migrating_event_triggers_persist_state`. ## Issue - Relates: #785 ## Test count changes | Suite | Before | After | Change | |---|---|---|---| | Unit | 127 | 338 | +211 | | Integration | 40 | 102 | +62 | | E2E | 65 | 37 | -28 | | **Total** | **232** | **477** | **+245** | ## Test plan - [x] CI passes --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 0e4816d commit 252eb4e

29 files changed

+1795
-1160
lines changed

.github/workflows/_tests.yaml

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
runs-on: ${{ matrix.os }}
3737

3838
env:
39-
TESTS_CONCURRENCY: "16"
39+
TESTS_CONCURRENCY: "8"
4040

4141
steps:
4242
- name: Checkout repository
@@ -85,11 +85,15 @@ jobs:
8585
(github.event_name == 'push' && github.ref == 'refs/heads/master')
8686
}}
8787
88+
# E2E tests build and run Actors on the platform. The combination of max-parallel 2 with 16 pytest
89+
# workers and a global concurrency group is a compromise between stability and performance - it keeps
90+
# the platform's resource usage in check while still allowing reasonable test throughput.
91+
concurrency:
92+
group: e2e-tests
93+
cancel-in-progress: false
94+
8895
strategy:
89-
# E2E tests build and run Actors on the platform. Limit parallel workflows to 1 to avoid exceeding
90-
# the platform's memory limits. A single workflow with 16 pytest workers provides good test
91-
# parallelization while staying within platform constraints.
92-
max-parallel: 1
96+
max-parallel: 2
9397
matrix:
9498
os: ["ubuntu-latest"]
9599
python-version: ["3.10", "3.14"]

src/apify/_utils.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ def get_system_info() -> dict:
2121
'os': sys.platform,
2222
}
2323

24-
if is_running_in_ipython():
25-
system_info['is_running_in_ipython'] = True
24+
system_info['is_running_in_ipython'] = is_running_in_ipython()
2625

2726
return system_info
2827

tests/e2e/test_actor_dataset.py

Lines changed: 0 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -2,151 +2,12 @@
22

33
from typing import TYPE_CHECKING
44

5-
from apify_shared.consts import ApifyEnvVars
6-
7-
from ._utils import generate_unique_resource_name
85
from apify import Actor
96

107
if TYPE_CHECKING:
11-
import pytest
12-
13-
from apify_client import ApifyClientAsync
14-
158
from .conftest import MakeActorFunction, RunActorFunction
169

1710

18-
async def test_push_and_verify_data_in_default_dataset(
19-
make_actor: MakeActorFunction,
20-
run_actor: RunActorFunction,
21-
) -> None:
22-
desired_item_count = 100 # Also change inside main() if you're changing this
23-
24-
async def main() -> None:
25-
desired_item_count = 100
26-
async with Actor:
27-
await Actor.push_data([{'id': i} for i in range(desired_item_count)])
28-
29-
actor = await make_actor(label='push-data', main_func=main)
30-
run_result = await run_actor(actor)
31-
32-
assert run_result.status == 'SUCCEEDED'
33-
34-
list_page = await actor.last_run().dataset().list_items()
35-
assert list_page.items[0]['id'] == 0
36-
assert list_page.items[-1]['id'] == desired_item_count - 1
37-
assert len(list_page.items) == list_page.count == desired_item_count
38-
39-
40-
async def test_push_large_data_chunks_over_9mb(
41-
make_actor: MakeActorFunction,
42-
run_actor: RunActorFunction,
43-
) -> None:
44-
async def main() -> None:
45-
async with Actor:
46-
await Actor.push_data([{'str': 'x' * 10000} for _ in range(5000)]) # ~50MB
47-
48-
actor = await make_actor(label='push-data-over-9mb', main_func=main)
49-
run_result = await run_actor(actor)
50-
51-
assert run_result.status == 'SUCCEEDED'
52-
53-
async for item in actor.last_run().dataset().iterate_items():
54-
assert item['str'] == 'x' * 10000
55-
56-
57-
async def test_dataset_iter_items(
58-
make_actor: MakeActorFunction,
59-
run_actor: RunActorFunction,
60-
) -> None:
61-
async def main() -> None:
62-
inserted_data = {'Something': 'something else'}
63-
64-
async with Actor:
65-
dataset = await Actor.open_dataset()
66-
await dataset.push_data(inserted_data)
67-
requested_data = [item async for item in dataset.iterate_items()]
68-
69-
assert len(requested_data) == 1
70-
assert requested_data[0] == inserted_data
71-
72-
actor = await make_actor(label='test_dataset_iter_items', main_func=main)
73-
run_result = await run_actor(actor)
74-
assert run_result.status == 'SUCCEEDED'
75-
76-
77-
async def test_same_references_in_default_dataset(
78-
make_actor: MakeActorFunction,
79-
run_actor: RunActorFunction,
80-
) -> None:
81-
async def main() -> None:
82-
async with Actor:
83-
dataset1 = await Actor.open_dataset()
84-
dataset2 = await Actor.open_dataset()
85-
assert dataset1 is dataset2
86-
87-
actor = await make_actor(label='dataset-same-ref-default', main_func=main)
88-
run_result = await run_actor(actor)
89-
90-
assert run_result.status == 'SUCCEEDED'
91-
92-
93-
async def test_same_references_in_named_dataset(
94-
make_actor: MakeActorFunction,
95-
run_actor: RunActorFunction,
96-
) -> None:
97-
dataset_name = generate_unique_resource_name('dataset')
98-
99-
async def main() -> None:
100-
async with Actor:
101-
input_object = await Actor.get_input()
102-
dataset_name = input_object['datasetName']
103-
dataset_by_name_1 = await Actor.open_dataset(name=dataset_name)
104-
dataset_by_name_2 = await Actor.open_dataset(name=dataset_name)
105-
assert dataset_by_name_1 is dataset_by_name_2
106-
107-
dataset_1_metadata = await dataset_by_name_1.get_metadata()
108-
dataset_by_id_1 = await Actor.open_dataset(id=dataset_1_metadata.id)
109-
dataset_by_id_2 = await Actor.open_dataset(id=dataset_1_metadata.id)
110-
assert dataset_by_id_1 is dataset_by_name_1
111-
assert dataset_by_id_2 is dataset_by_id_1
112-
113-
await dataset_by_name_1.drop()
114-
115-
actor = await make_actor(label='dataset-same-ref-named', main_func=main)
116-
run_result = await run_actor(actor, run_input={'datasetName': dataset_name})
117-
118-
assert run_result.status == 'SUCCEEDED'
119-
120-
121-
async def test_force_cloud(
122-
apify_client_async: ApifyClientAsync,
123-
monkeypatch: pytest.MonkeyPatch,
124-
) -> None:
125-
assert apify_client_async.token is not None
126-
monkeypatch.setenv(ApifyEnvVars.TOKEN, apify_client_async.token)
127-
128-
dataset_name = generate_unique_resource_name('dataset')
129-
dataset_item = {'foo': 'bar'}
130-
131-
async with Actor:
132-
dataset = await Actor.open_dataset(name=dataset_name, force_cloud=True)
133-
dataset_id = (await dataset.get_metadata()).id
134-
135-
await dataset.push_data(dataset_item)
136-
137-
dataset_client = apify_client_async.dataset(dataset_id)
138-
139-
try:
140-
dataset_details = await dataset_client.get()
141-
assert dataset_details is not None
142-
assert dataset_details.get('name') == dataset_name
143-
144-
dataset_items = await dataset_client.list_items()
145-
assert dataset_items.items == [dataset_item]
146-
finally:
147-
await dataset_client.delete()
148-
149-
15011
async def test_dataset_defaults(
15112
make_actor: MakeActorFunction,
15213
run_actor: RunActorFunction,

tests/e2e/test_actor_events.py

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -71,38 +71,3 @@ async def log_event(data: Any) -> None:
7171
]
7272
assert len(persist_state_events) > 2
7373
assert len(system_info_events) > 0
74-
75-
76-
async def test_event_listener_can_be_removed_successfully(
77-
make_actor: MakeActorFunction,
78-
run_actor: RunActorFunction,
79-
) -> None:
80-
async def main() -> None:
81-
import os
82-
from typing import Any
83-
84-
from apify_shared.consts import ApifyEnvVars
85-
from crawlee.events._types import Event
86-
87-
os.environ[ApifyEnvVars.PERSIST_STATE_INTERVAL_MILLIS] = '100'
88-
89-
counter = 0
90-
91-
def count_event(data: Any) -> None:
92-
nonlocal counter
93-
print(data)
94-
counter += 1
95-
96-
async with Actor:
97-
Actor.on(Event.PERSIST_STATE, count_event)
98-
await asyncio.sleep(0.5)
99-
assert counter > 1
100-
last_count = counter
101-
Actor.off(Event.PERSIST_STATE, count_event)
102-
await asyncio.sleep(0.5)
103-
assert counter == last_count
104-
105-
actor = await make_actor(label='actor-off-event', main_func=main)
106-
run_result = await run_actor(actor)
107-
108-
assert run_result.status == 'SUCCEEDED'

0 commit comments

Comments
 (0)