|
4 | 4 |
|
5 | 5 | import json
|
6 | 6 | from datetime import datetime
|
| 7 | +from unittest.mock import MagicMock |
7 | 8 |
|
8 | 9 | import pytest
|
9 | 10 | from airbyte_cdk.models import FailureType, SyncMode
|
10 | 11 | from airbyte_cdk.utils.traced_exception import AirbyteTracedException
|
11 | 12 | from facebook_business import FacebookAdsApi, FacebookSession
|
| 13 | +from facebook_business.exceptions import FacebookRequestError |
12 | 14 | from source_facebook_marketing.api import API
|
13 | 15 | from source_facebook_marketing.streams import AdAccount, AdCreatives, AdsInsights
|
| 16 | +from source_facebook_marketing.streams.common import traced_exception |
14 | 17 |
|
15 | 18 | FB_API_VERSION = FacebookAdsApi.API_VERSION
|
16 | 19 |
|
@@ -300,6 +303,17 @@ class TestRealErrors:
|
300 | 303 | # Potentially could be caused by some particular field (list of requested fields is constant).
|
301 | 304 | # But since sync was successful on next attempt, then conclusion is that this is a temporal problem.
|
302 | 305 | ),
|
| 306 | + ( |
| 307 | + "error_503_service_unavailable", |
| 308 | + { |
| 309 | + "json": { |
| 310 | + "error": { |
| 311 | + "message": "Call was not successful", |
| 312 | + } |
| 313 | + }, |
| 314 | + "status_code": 503, |
| 315 | + }, |
| 316 | + ), |
303 | 317 | ],
|
304 | 318 | )
|
305 | 319 | def test_retryable_error(self, some_config, requests_mock, name, retryable_error_response):
|
@@ -510,3 +524,35 @@ def test_adaccount_list_objects_retry(self, requests_mock, failure_response):
|
510 | 524 | stream_state={},
|
511 | 525 | )
|
512 | 526 | assert list(record_gen) == [{"account_id": "unknown_account", "id": "act_unknown_account"}]
|
| 527 | + |
| 528 | +def test_traced_exception_with_api_error(): |
| 529 | + error = FacebookRequestError( |
| 530 | + message="Some error occurred", |
| 531 | + request_context={}, |
| 532 | + http_status=400, |
| 533 | + http_headers={}, |
| 534 | + body='{"error": {"message": "Error validating access token", "code": 190}}' |
| 535 | + ) |
| 536 | + error.api_error_message = MagicMock(return_value="Error validating access token") |
| 537 | + |
| 538 | + result = traced_exception(error) |
| 539 | + |
| 540 | + assert isinstance(result, AirbyteTracedException) |
| 541 | + assert result.message == "Invalid access token. Re-authenticate if FB oauth is used or refresh access token with all required permissions" |
| 542 | + assert result.failure_type == FailureType.config_error |
| 543 | + |
| 544 | +def test_traced_exception_without_api_error(): |
| 545 | + error = FacebookRequestError( |
| 546 | + message="Call was unsuccessful. The Facebook API has imploded", |
| 547 | + request_context={}, |
| 548 | + http_status=408, |
| 549 | + http_headers={}, |
| 550 | + body='{}' |
| 551 | + ) |
| 552 | + error.api_error_message = MagicMock(return_value=None) |
| 553 | + |
| 554 | + result = traced_exception(error) |
| 555 | + |
| 556 | + assert isinstance(result, AirbyteTracedException) |
| 557 | + assert result.message == "Error code 408: Call was unsuccessful. The Facebook API has imploded." |
| 558 | + assert result.failure_type == FailureType.system_error |
0 commit comments