Skip to content

Commit 2b54d26

Browse files
vdusekclaude
andcommitted
test: Increase total test coverage and reduce E2E tests
Move 24 e2e tests that only exercise storage APIs or SDK logic to integration tests (real API, no Actor deployment). Delete 4 e2e tests already covered by existing unit/integration tests. Add 47 new unit tests covering ChargingManager, SmartApifyStorageClient, AliasResolver, ApifyEventManager, Configuration, crypto utilities, and Pydantic models. E2E: 65 -> 37 (-43%), Integration: 40 -> 102, Unit: 127 -> 295 Co-Authored-By: Claude Opus 4.6 <[email protected]>
1 parent 9293706 commit 2b54d26

18 files changed

+1446
-915
lines changed

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'

tests/e2e/test_actor_key_value_store.py

Lines changed: 0 additions & 173 deletions
Original file line numberDiff line numberDiff line change
@@ -2,150 +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_same_references_in_default_kvs(
19-
make_actor: MakeActorFunction,
20-
run_actor: RunActorFunction,
21-
) -> None:
22-
async def main() -> None:
23-
async with Actor:
24-
kvs1 = await Actor.open_key_value_store()
25-
kvs2 = await Actor.open_key_value_store()
26-
assert kvs1 is kvs2
27-
28-
actor = await make_actor(label='kvs-same-ref-default', main_func=main)
29-
run_result = await run_actor(actor)
30-
31-
assert run_result.status == 'SUCCEEDED'
32-
33-
34-
async def test_same_references_in_named_kvs(
35-
make_actor: MakeActorFunction,
36-
run_actor: RunActorFunction,
37-
) -> None:
38-
kvs_name = generate_unique_resource_name('key-value-store')
39-
40-
async def main() -> None:
41-
async with Actor:
42-
input_object = await Actor.get_input()
43-
kvs_name = input_object['kvsName']
44-
kvs_by_name_1 = await Actor.open_key_value_store(name=kvs_name)
45-
kvs_by_name_2 = await Actor.open_key_value_store(name=kvs_name)
46-
assert kvs_by_name_1 is kvs_by_name_2
47-
48-
kvs_1_metadata = await kvs_by_name_1.get_metadata()
49-
kvs_by_id_1 = await Actor.open_key_value_store(id=kvs_1_metadata.id)
50-
kvs_by_id_2 = await Actor.open_key_value_store(id=kvs_1_metadata.id)
51-
assert kvs_by_id_1 is kvs_by_name_1
52-
assert kvs_by_id_2 is kvs_by_id_1
53-
54-
await kvs_by_name_1.drop()
55-
56-
actor = await make_actor(label='kvs-same-ref-named', main_func=main)
57-
run_result = await run_actor(actor, run_input={'kvsName': kvs_name})
58-
59-
assert run_result.status == 'SUCCEEDED'
60-
61-
62-
async def test_force_cloud(
63-
apify_client_async: ApifyClientAsync,
64-
monkeypatch: pytest.MonkeyPatch,
65-
) -> None:
66-
assert apify_client_async.token is not None
67-
monkeypatch.setenv(ApifyEnvVars.TOKEN, apify_client_async.token)
68-
69-
key_value_store_name = generate_unique_resource_name('key_value_store')
70-
71-
async with Actor:
72-
key_value_store = await Actor.open_key_value_store(name=key_value_store_name, force_cloud=True)
73-
key_value_store_id = (await key_value_store.get_metadata()).id
74-
75-
await key_value_store.set_value('foo', 'bar')
76-
77-
key_value_store_client = apify_client_async.key_value_store(key_value_store_id)
78-
79-
try:
80-
key_value_store_details = await key_value_store_client.get()
81-
assert key_value_store_details is not None
82-
assert key_value_store_details.get('name') == key_value_store_name
83-
84-
key_value_store_record = await key_value_store_client.get_record('foo')
85-
assert key_value_store_record is not None
86-
assert key_value_store_record['value'] == 'bar'
87-
finally:
88-
await key_value_store_client.delete()
89-
90-
91-
async def test_set_and_get_value_in_same_run(
92-
make_actor: MakeActorFunction,
93-
run_actor: RunActorFunction,
94-
) -> None:
95-
async def main() -> None:
96-
async with Actor:
97-
await Actor.set_value('test', {'number': 123, 'string': 'a string', 'nested': {'test': 1}})
98-
value = await Actor.get_value('test')
99-
assert value['number'] == 123
100-
assert value['string'] == 'a string'
101-
assert value['nested']['test'] == 1
102-
103-
actor = await make_actor(label='actor-get-set-value', main_func=main)
104-
run_result = await run_actor(actor)
105-
106-
assert run_result.status == 'SUCCEEDED'
107-
108-
109-
async def test_set_value_in_one_run_and_get_value_in_another(
110-
make_actor: MakeActorFunction,
111-
run_actor: RunActorFunction,
112-
) -> None:
113-
async def main_set() -> None:
114-
async with Actor:
115-
await Actor.set_value('test', {'number': 123, 'string': 'a string', 'nested': {'test': 1}})
116-
117-
actor_set = await make_actor(label='actor-set-value', main_func=main_set)
118-
run_result_set = await run_actor(actor_set)
119-
120-
assert run_result_set.status == 'SUCCEEDED'
121-
122-
# Externally check if the value is present in key-value store
123-
test_record = await actor_set.last_run().key_value_store().get_record('test')
124-
assert test_record is not None
125-
test_value = test_record['value']
126-
assert test_value['number'] == 123
127-
assert test_value['string'] == 'a string'
128-
assert test_value['nested']['test'] == 1
129-
130-
async def main_get() -> None:
131-
async with Actor:
132-
input_object = await Actor.get_input()
133-
# Access KVS of the previous 'set' run
134-
kvs = await Actor.open_key_value_store(id=input_object['kvs-id'])
135-
value = await kvs.get_value('test')
136-
assert value['number'] == 123
137-
assert value['string'] == 'a string'
138-
assert value['nested']['test'] == 1
139-
140-
actor_get = await make_actor(label='actor-get-value', main_func=main_get)
141-
default_kvs_info = await actor_set.last_run().key_value_store().get()
142-
assert default_kvs_info is not None
143-
144-
run_result_get = await run_actor(actor_get, run_input={'kvs-id': default_kvs_info['id']})
145-
146-
assert run_result_get.status == 'SUCCEEDED'
147-
148-
14911
async def test_actor_get_input_from_run(
15012
make_actor: MakeActorFunction,
15113
run_actor: RunActorFunction,
@@ -197,41 +59,6 @@ async def main():
19759
assert run_result.status == 'SUCCEEDED'
19860

19961

200-
async def test_generate_public_url_for_kvs_record(
201-
make_actor: MakeActorFunction,
202-
run_actor: RunActorFunction,
203-
) -> None:
204-
async def main() -> None:
205-
from apify._crypto import create_hmac_signature
206-
from apify.storage_clients._apify._models import ApifyKeyValueStoreMetadata
207-
208-
async with Actor:
209-
public_api_url = Actor.configuration.api_public_base_url
210-
default_kvs_id = Actor.configuration.default_key_value_store_id
211-
record_key = 'public-record-key'
212-
213-
kvs = await Actor.open_key_value_store()
214-
metadata = await kvs.get_metadata()
215-
216-
assert isinstance(metadata, ApifyKeyValueStoreMetadata)
217-
assert metadata.url_signing_secret_key is not None
218-
219-
await kvs.set_value(record_key, {'exposedData': 'test'}, 'application/json')
220-
221-
record_url = await kvs.get_public_url(record_key)
222-
signature = create_hmac_signature(metadata.url_signing_secret_key, record_key)
223-
expected_record_url = (
224-
f'{public_api_url}/v2/key-value-stores/{default_kvs_id}/records/{record_key}?signature={signature}'
225-
)
226-
227-
assert record_url == expected_record_url
228-
229-
actor = await make_actor(label='kvs-get-public-url', main_func=main)
230-
run_result = await run_actor(actor)
231-
232-
assert run_result.status == 'SUCCEEDED'
233-
234-
23562
async def test_kvs_defaults(
23663
make_actor: MakeActorFunction,
23764
run_actor: RunActorFunction,

0 commit comments

Comments
 (0)