|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
3 | | -from datetime import UTC, datetime |
4 | | - |
5 | 3 | import apify_client.errors as client_errors |
6 | | -from apify_client._models import Run |
7 | | - |
8 | | -from apify.errors import ActorError, ActorRunError, ActorTimeoutError |
9 | | - |
10 | | - |
11 | | -def _make_run(*, status: str, exit_code: int | None = None, status_message: str | None = None) -> Run: |
12 | | - return Run.model_validate( |
13 | | - { |
14 | | - 'id': 'run123', |
15 | | - 'actId': 'act123', |
16 | | - 'userId': 'user123', |
17 | | - 'startedAt': datetime.now(UTC).isoformat(), |
18 | | - 'status': status, |
19 | | - 'statusMessage': status_message, |
20 | | - 'exitCode': exit_code, |
21 | | - 'meta': {'origin': 'DEVELOPMENT'}, |
22 | | - 'buildId': 'build123', |
23 | | - 'defaultDatasetId': 'ds123', |
24 | | - 'defaultKeyValueStoreId': 'kvs123', |
25 | | - 'defaultRequestQueueId': 'rq123', |
26 | | - 'containerUrl': 'https://container', |
27 | | - 'buildNumber': '0.0.1', |
28 | | - 'generalAccess': 'RESTRICTED', |
29 | | - 'stats': {'restartCount': 0, 'resurrectCount': 0, 'computeUnits': 1}, |
30 | | - 'options': {'build': 'latest', 'timeoutSecs': 4, 'memoryMbytes': 1024, 'diskMbytes': 1024}, |
31 | | - } |
32 | | - ) |
33 | | - |
34 | | - |
35 | | -def test_actor_error_defaults() -> None: |
36 | | - error = ActorError('something went wrong') |
37 | | - assert error.code == 'actor-error' |
38 | | - assert error.retryable is False |
39 | | - assert str(error) == 'something went wrong' |
40 | | - |
41 | | - |
42 | | -def test_actor_error_overrides_are_instance_scoped() -> None: |
43 | | - error = ActorError('boom', code='custom', retryable=True) |
44 | | - assert error.code == 'custom' |
45 | | - assert error.retryable is True |
46 | | - # Overriding on an instance must not leak to the class default. |
47 | | - assert ActorError.code == 'actor-error' |
48 | | - assert ActorError.retryable is False |
49 | | - |
50 | 4 |
|
51 | | -def test_actor_run_error_is_actor_error() -> None: |
52 | | - assert issubclass(ActorRunError, ActorError) |
53 | | - assert ActorRunError.code == 'actor-run-failed' |
54 | | - assert ActorRunError.retryable is False |
55 | | - |
56 | | - |
57 | | -def test_actor_timeout_error_is_actor_run_error() -> None: |
58 | | - assert issubclass(ActorTimeoutError, ActorRunError) |
59 | | - assert ActorTimeoutError.code == 'actor-timed-out' |
60 | | - assert ActorTimeoutError.retryable is True |
61 | | - |
62 | | - |
63 | | -def test_actor_run_error_carries_run_metadata() -> None: |
64 | | - run = _make_run(status='FAILED', exit_code=1, status_message='Actor crashed') |
65 | | - error = ActorRunError(run) |
66 | | - assert error.run_id == 'run123' |
67 | | - assert error.status == 'FAILED' |
68 | | - assert error.exit_code == 1 |
69 | | - assert error.status_message == 'Actor crashed' |
70 | | - assert error.retryable is False |
71 | | - assert 'run123' in str(error) |
72 | | - assert 'Actor crashed' in str(error) |
73 | | - |
74 | | - |
75 | | -def test_actor_run_error_from_run_failed() -> None: |
76 | | - error = ActorRunError.from_run(_make_run(status='FAILED')) |
77 | | - assert type(error) is ActorRunError |
78 | | - assert not error.retryable |
79 | | - |
80 | | - |
81 | | -def test_actor_run_error_from_run_timed_out() -> None: |
82 | | - error = ActorRunError.from_run(_make_run(status='TIMED-OUT')) |
83 | | - assert isinstance(error, ActorTimeoutError) |
84 | | - assert error.retryable is True |
85 | | - assert error.run_id == 'run123' |
86 | | - assert error.code == 'actor-timed-out' |
| 5 | +import apify.errors as sdk_errors |
87 | 6 |
|
88 | 7 |
|
89 | 8 | def test_client_errors_are_re_exported() -> None: |
90 | 9 | """`apify.errors` re-exports the API client error hierarchy so callers have a single import location.""" |
91 | | - from apify.errors import ApifyApiError, ApifyClientError, NotFoundError, RateLimitError |
92 | | - |
93 | | - assert ApifyApiError is client_errors.ApifyApiError |
94 | | - assert ApifyClientError is client_errors.ApifyClientError |
95 | | - assert NotFoundError is client_errors.NotFoundError |
96 | | - assert RateLimitError is client_errors.RateLimitError |
97 | | - # The re-exported API errors are independent of the SDK's own `ActorError` tree. |
98 | | - assert not issubclass(client_errors.ApifyApiError, ActorError) |
| 10 | + names = [ |
| 11 | + 'ApifyApiError', |
| 12 | + 'ApifyClientError', |
| 13 | + 'ConflictError', |
| 14 | + 'ForbiddenError', |
| 15 | + 'InvalidRequestError', |
| 16 | + 'InvalidResponseBodyError', |
| 17 | + 'NotFoundError', |
| 18 | + 'RateLimitError', |
| 19 | + 'ServerError', |
| 20 | + 'UnauthorizedError', |
| 21 | + ] |
| 22 | + assert set(sdk_errors.__all__) == set(names) |
| 23 | + for name in names: |
| 24 | + assert getattr(sdk_errors, name) is getattr(client_errors, name) |
0 commit comments