diff --git a/airbyte-cdk/python/airbyte_cdk/sources/abstract_source.py b/airbyte-cdk/python/airbyte_cdk/sources/abstract_source.py index f4087c81dcec..4beec964909f 100644 --- a/airbyte-cdk/python/airbyte_cdk/sources/abstract_source.py +++ b/airbyte-cdk/python/airbyte_cdk/sources/abstract_source.py @@ -79,13 +79,9 @@ def check(self, logger: logging.Logger, config: Mapping[str, Any]) -> AirbyteCon """Implements the Check Connection operation from the Airbyte Specification. See https://docs.airbyte.com/understanding-airbyte/airbyte-protocol/#check. """ - try: - check_succeeded, error = self.check_connection(logger, config) - if not check_succeeded: - return AirbyteConnectionStatus(status=Status.FAILED, message=repr(error)) - except Exception as e: - return AirbyteConnectionStatus(status=Status.FAILED, message=repr(e)) - + check_succeeded, error = self.check_connection(logger, config) + if not check_succeeded: + return AirbyteConnectionStatus(status=Status.FAILED, message=repr(error)) return AirbyteConnectionStatus(status=Status.SUCCEEDED) def read( diff --git a/airbyte-cdk/python/unit_tests/sources/test_abstract_source.py b/airbyte-cdk/python/unit_tests/sources/test_abstract_source.py index f0cb76516282..e9af077f02e8 100644 --- a/airbyte-cdk/python/unit_tests/sources/test_abstract_source.py +++ b/airbyte-cdk/python/unit_tests/sources/test_abstract_source.py @@ -104,10 +104,11 @@ def test_failed_check(): assert expected == MockSource(check_lambda=lambda: (False, "womp womp")).check(logger, {}) -def test_raising_check(): +def test_raising_check(mocker): """Tests that if a source raises an unexpected exception the appropriate connectionStatus failure message is returned.""" - expected = AirbyteConnectionStatus(status=Status.FAILED, message="Exception('this should fail')") - assert expected == MockSource(check_lambda=lambda: exec('raise Exception("this should fail")')).check(logger, {}) + check_lambda = mocker.Mock(side_effect=BaseException("this should fail")) + with pytest.raises(BaseException): + MockSource(check_lambda=check_lambda).check(logger, {}) class MockStream(Stream): @@ -334,10 +335,7 @@ def test_valid_full_refresh_read_with_slices(mocker): @pytest.mark.parametrize( "slices", - [ - [{"1": "1"}, {"2": "2"}], - [{"date": datetime.date(year=2023, month=1, day=1)}, {"date": datetime.date(year=2023, month=1, day=1)}] - ] + [[{"1": "1"}, {"2": "2"}], [{"date": datetime.date(year=2023, month=1, day=1)}, {"date": datetime.date(year=2023, month=1, day=1)}]], ) def test_read_full_refresh_with_slices_sends_slice_messages(mocker, slices): """Given the logger is debug and a full refresh, AirbyteMessages are sent for slices""" @@ -369,7 +367,7 @@ def test_read_incremental_with_slices_sends_slice_messages(mocker): debug_logger.setLevel(logging.DEBUG) slices = [{"1": "1"}, {"2": "2"}] stream = MockStream( - [({"sync_mode": SyncMode.incremental, "stream_slice": s, 'stream_state': {}}, [s]) for s in slices], + [({"sync_mode": SyncMode.incremental, "stream_slice": s, "stream_state": {}}, [s]) for s in slices], name="s1", ) diff --git a/airbyte-integrations/bases/connector-acceptance-test/CHANGELOG.md b/airbyte-integrations/bases/connector-acceptance-test/CHANGELOG.md index 2474d8ef9758..4137f269e586 100644 --- a/airbyte-integrations/bases/connector-acceptance-test/CHANGELOG.md +++ b/airbyte-integrations/bases/connector-acceptance-test/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## 0.7.2 +TestConnection: assert that a check with `exception` status emits a trace message. + ## 0.7.1 Discovery backward compatibility tests: handle errors on previous connectors catalog retrieval. Return None when the discovery failed. It should unblock the situation when tests fails even if you bypassed backward compatibility tests. diff --git a/airbyte-integrations/bases/connector-acceptance-test/Dockerfile b/airbyte-integrations/bases/connector-acceptance-test/Dockerfile index 86438d55e65e..419a1430fb66 100644 --- a/airbyte-integrations/bases/connector-acceptance-test/Dockerfile +++ b/airbyte-integrations/bases/connector-acceptance-test/Dockerfile @@ -33,7 +33,7 @@ COPY pytest.ini setup.py ./ COPY connector_acceptance_test ./connector_acceptance_test RUN pip install . -LABEL io.airbyte.version=0.7.1 +LABEL io.airbyte.version=0.7.2 LABEL io.airbyte.name=airbyte/connector-acceptance-test ENTRYPOINT ["python", "-m", "pytest", "-p", "connector_acceptance_test.plugin", "-r", "fEsx"] diff --git a/airbyte-integrations/bases/connector-acceptance-test/connector_acceptance_test/tests/test_core.py b/airbyte-integrations/bases/connector-acceptance-test/connector_acceptance_test/tests/test_core.py index 69e6c7a8ffc5..852f603126eb 100644 --- a/airbyte-integrations/bases/connector-acceptance-test/connector_acceptance_test/tests/test_core.py +++ b/airbyte-integrations/bases/connector-acceptance-test/connector_acceptance_test/tests/test_core.py @@ -18,6 +18,7 @@ from airbyte_cdk.models import ( AirbyteRecordMessage, AirbyteStream, + AirbyteTraceMessage, ConfiguredAirbyteCatalog, ConfiguredAirbyteStream, ConnectorSpecification, @@ -46,7 +47,6 @@ find_keyword_schema, ) from connector_acceptance_test.utils.json_schema_helper import JsonSchemaHelper, get_expected_schema_structure, get_object_structure -from docker.errors import ContainerError from jsonschema._utils import flatten @@ -488,11 +488,13 @@ def test_check(self, connector_config, inputs: ConnectionTestConfig, docker_runn assert len(con_messages) == 1, "Connection status message should be emitted exactly once" assert con_messages[0].connectionStatus.status == Status.FAILED elif inputs.status == ConnectionTestConfig.Status.Exception: - with pytest.raises(ContainerError) as err: - docker_runner.call_check(config=connector_config) - - assert err.value.exit_status != 0, "Connector should exit with error code" - assert "Traceback" in err.value.stderr, "Connector should print exception" + output = docker_runner.call_check(config=connector_config, raise_container_error=False) + trace_messages = filter_output(output, Type.TRACE) + assert len(trace_messages) == 1, "A trace message should be emitted in case of unexpected errors" + trace = trace_messages[0].trace + assert isinstance(trace, AirbyteTraceMessage) + assert trace.error is not None + assert trace.error.message is not None @pytest.mark.default_timeout(30) diff --git a/airbyte-integrations/connectors/source-airtable/acceptance-test-config.yml b/airbyte-integrations/connectors/source-airtable/acceptance-test-config.yml index 9d1a5325f09e..03e2909a6b72 100644 --- a/airbyte-integrations/connectors/source-airtable/acceptance-test-config.yml +++ b/airbyte-integrations/connectors/source-airtable/acceptance-test-config.yml @@ -15,7 +15,9 @@ acceptance_tests: - config_path: "secrets/config_oauth.json" status: "succeed" - config_path: "integration_tests/invalid_config_oauth.json" - status: "failed" + status: "failed" + - config_path: "integration_tests/invalid_config_oauth_missing_fields.json" + status: "exception" discovery: tests: - config_path: "secrets/config.json" @@ -31,7 +33,7 @@ acceptance_tests: path: "integration_tests/expected_records.jsonl" extra_fields: true exact_order: true - extra_records: false + extra_records: false - config_path: "secrets/config_oauth.json" expect_records: path: "integration_tests/expected_records.jsonl" diff --git a/airbyte-integrations/connectors/source-airtable/integration_tests/invalid_config_oauth_missing_fields.json b/airbyte-integrations/connectors/source-airtable/integration_tests/invalid_config_oauth_missing_fields.json new file mode 100644 index 000000000000..2c79d103dfef --- /dev/null +++ b/airbyte-integrations/connectors/source-airtable/integration_tests/invalid_config_oauth_missing_fields.json @@ -0,0 +1,9 @@ +{ + "credentials": { + "auth_method": "oauth2.0", + "client_id": "client_id", + "client_secret": "client_secret", + "refresh_token": "refresh_token", + "token_expiry_date": "2023-01-01T12:12:12.000000+00:00" + } +} diff --git a/airbyte-integrations/connectors/source-amazon-ads/acceptance-test-config.yml b/airbyte-integrations/connectors/source-amazon-ads/acceptance-test-config.yml index c34c0920e194..2927077ed4a5 100644 --- a/airbyte-integrations/connectors/source-amazon-ads/acceptance-test-config.yml +++ b/airbyte-integrations/connectors/source-amazon-ads/acceptance-test-config.yml @@ -1,62 +1,62 @@ acceptance_tests: basic_read: tests: - - config_path: secrets/config.json - empty_streams: - - name: sponsored_brands_ad_groups - bypass_reason: "can't populate stream because it requires real ad campaign" - - name: sponsored_brands_campaigns - bypass_reason: "can't populate stream because it requires real ad campaign" - - name: sponsored_brands_keywords - bypass_reason: "can't populate stream because it requires real ad campaign" - - name: attribution_report_performance_creative - bypass_reason: "can't populate stream because it requires real ad campaign" - - name: attribution_report_performance_adgroup - bypass_reason: "can't populate stream because it requires real ad campaign" - - name: attribution_report_products - bypass_reason: "can't populate stream because it requires real ad campaign" - - name: attribution_report_performance_campaign - bypass_reason: "can't populate stream because it requires real ad campaign" - - name: sponsored_display_report_stream - bypass_reason: "can't populate stream because it requires real ad campaign" - - name: sponsored_brands_report_stream - bypass_reason: "can't populate stream because it requires real ad campaign" - - name: sponsored_brands_video_report_stream - bypass_reason: "can't populate stream because it requires real ad campaign" - - name: sponsored_products_report_stream - bypass_reason: "can't populate stream because it requires real ad campaign" - timeout_seconds: 2400 - expect_records: - path: integration_tests/expected_records.jsonl + - config_path: secrets/config.json + empty_streams: + - name: sponsored_brands_ad_groups + bypass_reason: "can't populate stream because it requires real ad campaign" + - name: sponsored_brands_campaigns + bypass_reason: "can't populate stream because it requires real ad campaign" + - name: sponsored_brands_keywords + bypass_reason: "can't populate stream because it requires real ad campaign" + - name: attribution_report_performance_creative + bypass_reason: "can't populate stream because it requires real ad campaign" + - name: attribution_report_performance_adgroup + bypass_reason: "can't populate stream because it requires real ad campaign" + - name: attribution_report_products + bypass_reason: "can't populate stream because it requires real ad campaign" + - name: attribution_report_performance_campaign + bypass_reason: "can't populate stream because it requires real ad campaign" + - name: sponsored_display_report_stream + bypass_reason: "can't populate stream because it requires real ad campaign" + - name: sponsored_brands_report_stream + bypass_reason: "can't populate stream because it requires real ad campaign" + - name: sponsored_brands_video_report_stream + bypass_reason: "can't populate stream because it requires real ad campaign" + - name: sponsored_products_report_stream + bypass_reason: "can't populate stream because it requires real ad campaign" + timeout_seconds: 2400 + expect_records: + path: integration_tests/expected_records.jsonl connection: tests: - - config_path: secrets/config.json - status: succeed - - config_path: integration_tests/invalid_config.json - status: failed + - config_path: secrets/config.json + status: succeed + - config_path: integration_tests/invalid_config.json + status: exception discovery: tests: - - config_path: secrets/config.json + - config_path: secrets/config.json full_refresh: tests: - - config_path: secrets/config.json - configured_catalog_path: integration_tests/configured_catalog.json - - config_path: secrets/config_report.json - configured_catalog_path: integration_tests/configured_catalog_report.json - timeout_seconds: 3600 + - config_path: secrets/config.json + configured_catalog_path: integration_tests/configured_catalog.json + - config_path: secrets/config_report.json + configured_catalog_path: integration_tests/configured_catalog_report.json + timeout_seconds: 3600 incremental: tests: - - config_path: secrets/config_report.json - configured_catalog_path: integration_tests/configured_catalog_report.json - cursor_paths: - sponsored_products_report_stream: - - '1861552880916640' - - reportDate - future_state: - future_state_path: integration_tests/abnormal_state.json - timeout_seconds: 2400 + - config_path: secrets/config_report.json + configured_catalog_path: integration_tests/configured_catalog_report.json + cursor_paths: + sponsored_products_report_stream: + - "1861552880916640" + - reportDate + future_state: + future_state_path: integration_tests/abnormal_state.json + timeout_seconds: 2400 spec: tests: - - spec_path: integration_tests/spec.json + - spec_path: integration_tests/spec.json connector_image: airbyte/source-amazon-ads:dev test_strictness_level: high diff --git a/airbyte-integrations/connectors/source-google-ads/acceptance-test-config.yml b/airbyte-integrations/connectors/source-google-ads/acceptance-test-config.yml index ce59965caabc..0bfaeb98b1ad 100644 --- a/airbyte-integrations/connectors/source-google-ads/acceptance-test-config.yml +++ b/airbyte-integrations/connectors/source-google-ads/acceptance-test-config.yml @@ -11,7 +11,7 @@ acceptance_tests: - config_path: "secrets/config.json" status: "succeed" - config_path: "integration_tests/invalid_config.json" - status: "failed" + status: "exception" discovery: tests: - config_path: "secrets/config.json" diff --git a/airbyte-integrations/connectors/source-google-analytics-v4/acceptance-test-config.yml b/airbyte-integrations/connectors/source-google-analytics-v4/acceptance-test-config.yml index 6ca6d9b8fd58..070c9efc0a82 100644 --- a/airbyte-integrations/connectors/source-google-analytics-v4/acceptance-test-config.yml +++ b/airbyte-integrations/connectors/source-google-analytics-v4/acceptance-test-config.yml @@ -1,37 +1,37 @@ acceptance_tests: basic_read: tests: - - config_path: secrets/service_config.json - empty_streams: - - name: users_per_city - bypass_reason: no records in the stream - expect_records: - path: integration_tests/expected_records.jsonl - timeout_seconds: 1800 + - config_path: secrets/service_config.json + empty_streams: + - name: users_per_city + bypass_reason: no records in the stream + expect_records: + path: integration_tests/expected_records.jsonl + timeout_seconds: 1800 connection: tests: - - config_path: secrets/service_config.json - status: succeed - - config_path: secrets/old_config.json - status: succeed - - config_path: integration_tests/invalid_config.json - status: failed + - config_path: secrets/service_config.json + status: succeed + - config_path: secrets/old_config.json + status: succeed + - config_path: integration_tests/invalid_config.json + status: exception discovery: tests: - - config_path: secrets/service_config.json + - config_path: secrets/service_config.json full_refresh: tests: - - config_path: secrets/service_config.json - configured_catalog_path: integration_tests/configured_catalog.json + - config_path: secrets/service_config.json + configured_catalog_path: integration_tests/configured_catalog.json incremental: tests: - - config_path: secrets/service_config.json - configured_catalog_path: integration_tests/configured_catalog.json - future_state: - future_state_path: integration_tests/abnormal_state.json - threshold_days: 2 + - config_path: secrets/service_config.json + configured_catalog_path: integration_tests/configured_catalog.json + future_state: + future_state_path: integration_tests/abnormal_state.json + threshold_days: 2 spec: tests: - - spec_path: source_google_analytics_v4/spec.json + - spec_path: source_google_analytics_v4/spec.json connector_image: airbyte/source-google-analytics-v4:dev test_strictness_level: high diff --git a/airbyte-integrations/connectors/source-marketo/acceptance-test-config.yml b/airbyte-integrations/connectors/source-marketo/acceptance-test-config.yml index ecf753bc5627..5e1805a3c798 100644 --- a/airbyte-integrations/connectors/source-marketo/acceptance-test-config.yml +++ b/airbyte-integrations/connectors/source-marketo/acceptance-test-config.yml @@ -11,7 +11,7 @@ acceptance_tests: - config_path: "secrets/config.json" status: "succeed" - config_path: "integration_tests/invalid_config.json" - status: "failed" + status: "exception" discovery: tests: - config_path: "secrets/config.json" diff --git a/airbyte-integrations/connectors/source-paypal-transaction/acceptance-test-config.yml b/airbyte-integrations/connectors/source-paypal-transaction/acceptance-test-config.yml index 8a02531663bf..50cd23c2ddf5 100644 --- a/airbyte-integrations/connectors/source-paypal-transaction/acceptance-test-config.yml +++ b/airbyte-integrations/connectors/source-paypal-transaction/acceptance-test-config.yml @@ -9,36 +9,36 @@ acceptance_tests: - config_path: secrets/config.json status: succeed - config_path: integration_tests/invalid_config.json - status: failed + status: exception - config_path: secrets/config_oauth.json status: succeed - config_path: integration_tests/invalid_config_oauth.json - status: failed + status: exception discovery: tests: - - config_path: secrets/config.json + - config_path: secrets/config.json basic_read: tests: - - config_path: secrets/config.json - empty_streams: - - name: balances - bypass_reason: "value of 'last_refresh_time' field changes during every read" - timeout_seconds: 1200 - expect_records: - path: "integration_tests/expected_records.jsonl" - extra_fields: no - exact_order: no - extra_records: yes + - config_path: secrets/config.json + empty_streams: + - name: balances + bypass_reason: "value of 'last_refresh_time' field changes during every read" + timeout_seconds: 1200 + expect_records: + path: "integration_tests/expected_records.jsonl" + extra_fields: no + exact_order: no + extra_records: yes incremental: tests: - - config_path: secrets/config.json - configured_catalog_path: integration_tests/configured_catalog.json - future_state: - future_state_path: integration_tests/abnormal_state.json - cursor_paths: - transactions: [ "date" ] - balances: [ "date" ] + - config_path: secrets/config.json + configured_catalog_path: integration_tests/configured_catalog.json + future_state: + future_state_path: integration_tests/abnormal_state.json + cursor_paths: + transactions: ["date"] + balances: ["date"] full_refresh: tests: - - config_path: secrets/config.json - configured_catalog_path: integration_tests/configured_catalog.json \ No newline at end of file + - config_path: secrets/config.json + configured_catalog_path: integration_tests/configured_catalog.json diff --git a/airbyte-integrations/connectors/source-pinterest/acceptance-test-config.yml b/airbyte-integrations/connectors/source-pinterest/acceptance-test-config.yml index bea45f8c41db..3b6ad0cb0be1 100644 --- a/airbyte-integrations/connectors/source-pinterest/acceptance-test-config.yml +++ b/airbyte-integrations/connectors/source-pinterest/acceptance-test-config.yml @@ -1,64 +1,64 @@ acceptance_tests: basic_read: tests: - - config_path: secrets/config.json - empty_streams: - - bypass_reason: The stream could return 0 records, because of low rate-limits - name: ad_account_analytics - - bypass_reason: The stream could return 0 records, because of low rate-limits - name: ad_analytics - - bypass_reason: The stream could return 0 records, because of low rate-limits - name: ad_group_analytics - - bypass_reason: The stream could return 0 records, because of low rate-limits - name: ad_groups - - bypass_reason: The stream could return 0 records, because of low rate-limits - name: ads - - bypass_reason: The stream could return 0 records, because of low rate-limits - name: board_section_pins - - bypass_reason: The stream could return 0 records, because of low rate-limits - name: board_sections - - bypass_reason: The stream could return 0 records, because of low rate-limits - name: campaign_analytics - - bypass_reason: The stream could return 0 records, because of low rate-limits - name: campaigns - - bypass_reason: The stream could return 0 records, because of low rate-limits - name: user_account_analytics - timeout_seconds: 1200 - expect_records: - path: "integration_tests/expected_records.jsonl" - extra_fields: no - exact_order: no - extra_records: yes + - config_path: secrets/config.json + empty_streams: + - bypass_reason: The stream could return 0 records, because of low rate-limits + name: ad_account_analytics + - bypass_reason: The stream could return 0 records, because of low rate-limits + name: ad_analytics + - bypass_reason: The stream could return 0 records, because of low rate-limits + name: ad_group_analytics + - bypass_reason: The stream could return 0 records, because of low rate-limits + name: ad_groups + - bypass_reason: The stream could return 0 records, because of low rate-limits + name: ads + - bypass_reason: The stream could return 0 records, because of low rate-limits + name: board_section_pins + - bypass_reason: The stream could return 0 records, because of low rate-limits + name: board_sections + - bypass_reason: The stream could return 0 records, because of low rate-limits + name: campaign_analytics + - bypass_reason: The stream could return 0 records, because of low rate-limits + name: campaigns + - bypass_reason: The stream could return 0 records, because of low rate-limits + name: user_account_analytics + timeout_seconds: 1200 + expect_records: + path: "integration_tests/expected_records.jsonl" + extra_fields: no + exact_order: no + extra_records: yes connection: tests: - - config_path: secrets/config.json - status: succeed - - config_path: integration_tests/invalid_config.json - status: failed - - config_path: secrets/config_oauth.json - status: succeed + - config_path: secrets/config.json + status: succeed + - config_path: integration_tests/invalid_config.json + status: exception + - config_path: secrets/config_oauth.json + status: succeed discovery: tests: - - backward_compatibility_tests_config: - disable_for_version: 0.1.2 - config_path: secrets/config.json - - backward_compatibility_tests_config: - disable_for_version: 0.1.2 - config_path: secrets/config_oauth.json + - backward_compatibility_tests_config: + disable_for_version: 0.1.2 + config_path: secrets/config.json + - backward_compatibility_tests_config: + disable_for_version: 0.1.2 + config_path: secrets/config_oauth.json full_refresh: tests: - - config_path: secrets/config.json - configured_catalog_path: integration_tests/configured_catalog.json + - config_path: secrets/config.json + configured_catalog_path: integration_tests/configured_catalog.json incremental: tests: - - config_path: secrets/config.json - configured_catalog_path: integration_tests/configured_catalog.json - future_state: - future_state_path: integration_tests/abnormal_state.json + - config_path: secrets/config.json + configured_catalog_path: integration_tests/configured_catalog.json + future_state: + future_state_path: integration_tests/abnormal_state.json spec: tests: - - backward_compatibility_tests_config: - disable_for_version: 0.1.2 - spec_path: source_pinterest/spec.json + - backward_compatibility_tests_config: + disable_for_version: 0.1.2 + spec_path: source_pinterest/spec.json connector_image: airbyte/source-pinterest:dev test_strictness_level: high diff --git a/airbyte-integrations/connectors/source-zendesk-talk/acceptance-test-config.yml b/airbyte-integrations/connectors/source-zendesk-talk/acceptance-test-config.yml index c3c75ed43f08..00506e5421f1 100644 --- a/airbyte-integrations/connectors/source-zendesk-talk/acceptance-test-config.yml +++ b/airbyte-integrations/connectors/source-zendesk-talk/acceptance-test-config.yml @@ -12,7 +12,7 @@ acceptance_tests: - config_path: "secrets/config.json" status: "succeed" - config_path: "integration_tests/invalid_config.json" - status: "failed" + status: "exception" - config_path: "secrets/config_old.json" status: "succeed" discovery: @@ -30,17 +30,17 @@ acceptance_tests: path: "integration_tests/expected_records.jsonl" ignored_fields: greetings: - - name: audio_url - bypass_reason: In url present auto generated hash + - name: audio_url + bypass_reason: In url present auto generated hash account_overview: - - name: current_timestamp - bypass_reason: Depend on current time + - name: current_timestamp + bypass_reason: Depend on current time agents_overview: - - name: current_timestamp - bypass_reason: Depend on current time + - name: current_timestamp + bypass_reason: Depend on current time current_queue_activity: - - name: current_timestamp - bypass_reason: Depend on current time + - name: current_timestamp + bypass_reason: Depend on current time incremental: tests: - config_path: "secrets/config.json" @@ -52,11 +52,11 @@ acceptance_tests: configured_catalog_path: "integration_tests/configured_catalog.json" ignored_fields: account_overview: - - name: current_timestamp - bypass_reason: Depend on current time + - name: current_timestamp + bypass_reason: Depend on current time agents_overview: - - name: current_timestamp - bypass_reason: Depend on current time + - name: current_timestamp + bypass_reason: Depend on current time current_queue_activity: - - name: current_timestamp - bypass_reason: Depend on current time + - name: current_timestamp + bypass_reason: Depend on current time