1111from dataclasses import asdict
1212from pathlib import Path
1313from subprocess import CompletedProcess , SubprocessError
14- from typing import Literal
14+ from typing import Literal , cast
1515
1616import orjson
1717import pytest
2525 DestinationSyncMode ,
2626 SyncMode ,
2727)
28- from airbyte_cdk .models .airbyte_protocol_serializers import (
29- AirbyteCatalogSerializer ,
30- AirbyteStreamSerializer ,
31- )
3228from airbyte_cdk .models .connector_metadata import MetadataFile
3329from airbyte_cdk .test .entrypoint_wrapper import EntrypointOutput
3430from airbyte_cdk .test .models import ConnectorTestScenario
35- from airbyte_cdk .test .utils .reading import catalog
3631from airbyte_cdk .utils .connector_paths import (
3732 ACCEPTANCE_TEST_CONFIG ,
3833 find_connector_root ,
3934)
40- from airbyte_cdk .utils .docker import build_connector_image , run_docker_command
35+ from airbyte_cdk .utils .docker import (
36+ build_connector_image ,
37+ run_docker_airbyte_command ,
38+ run_docker_command ,
39+ )
4140
4241
4342class DockerConnectorTestSuite :
@@ -55,6 +54,17 @@ def get_connector_root_dir(cls) -> Path:
5554 """Get the root directory of the connector."""
5655 return find_connector_root ([cls .get_test_class_dir (), Path .cwd ()])
5756
57+ @classproperty
58+ def connector_name (self ) -> str :
59+ """Get the name of the connector."""
60+ connector_root = self .get_connector_root_dir ()
61+ return connector_root .absolute ().name
62+
63+ @classmethod
64+ def is_destination_connector (cls ) -> bool :
65+ """Check if the connector is a destination."""
66+ return cast (str , cls .connector_name ).startswith ("destination-" )
67+
5868 @classproperty
5969 def acceptance_test_config_path (cls ) -> Path :
6070 """Get the path to the acceptance test config file."""
@@ -145,23 +155,16 @@ def test_docker_image_build_and_spec(
145155 no_verify = False ,
146156 )
147157
148- try :
149- result : CompletedProcess [str ] = run_docker_command (
150- [
151- "docker" ,
152- "run" ,
153- "--rm" ,
154- connector_image ,
155- "spec" ,
156- ],
157- check = True , # Raise an error if the command fails
158- capture_stderr = True ,
159- capture_stdout = True ,
160- )
161- except SubprocessError as ex :
162- raise AssertionError (
163- f"Failed to run `spec` command in docker image { connector_image !r} . Error: { ex !s} "
164- ) from None
158+ _ = run_docker_airbyte_command (
159+ [
160+ "docker" ,
161+ "run" ,
162+ "--rm" ,
163+ connector_image ,
164+ "spec" ,
165+ ],
166+ raise_if_errors = True ,
167+ )
165168
166169 @pytest .mark .skipif (
167170 shutil .which ("docker" ) is None ,
@@ -203,7 +206,7 @@ def test_docker_image_build_and_check(
203206 with scenario .with_temp_config_file (
204207 connector_root = connector_root ,
205208 ) as temp_config_file :
206- _ = run_docker_command (
209+ _ = run_docker_airbyte_command (
207210 [
208211 "docker" ,
209212 "run" ,
@@ -215,9 +218,7 @@ def test_docker_image_build_and_check(
215218 "--config" ,
216219 container_config_path ,
217220 ],
218- check = True , # Raise an error if the command fails
219- capture_stderr = True ,
220- capture_stdout = True ,
221+ raise_if_errors = True ,
221222 )
222223
223224 @pytest .mark .skipif (
@@ -242,6 +243,9 @@ def test_docker_image_build_and_read(
242243 the local docker image cache using `docker image prune -a` command.
243244 - If the --connector-image arg is provided, it will be used instead of building the image.
244245 """
246+ if self .is_destination_connector ():
247+ pytest .skip ("Skipping read test for destination connector." )
248+
245249 if scenario .expected_outcome .expect_exception ():
246250 pytest .skip ("Skipping (expected to fail)." )
247251
@@ -295,7 +299,7 @@ def test_docker_image_build_and_read(
295299 ) as temp_dir_str ,
296300 ):
297301 temp_dir = Path (temp_dir_str )
298- discover_result = run_docker_command (
302+ discover_result = run_docker_airbyte_command (
299303 [
300304 "docker" ,
301305 "run" ,
@@ -307,20 +311,12 @@ def test_docker_image_build_and_read(
307311 "--config" ,
308312 container_config_path ,
309313 ],
310- check = True , # Raise an error if the command fails
311- capture_stderr = True ,
312- capture_stdout = True ,
314+ raise_if_errors = True ,
313315 )
314- parsed_output = EntrypointOutput (messages = discover_result .stdout .splitlines ())
315- try :
316- catalog_message = parsed_output .catalog # Get catalog message
317- assert catalog_message .catalog is not None , "Catalog message missing catalog."
318- discovered_catalog : AirbyteCatalog = parsed_output .catalog .catalog
319- except Exception as ex :
320- raise AssertionError (
321- f"Failed to load discovered catalog from { discover_result .stdout } . "
322- f"Error: { ex !s} "
323- ) from None
316+
317+ catalog_message = discover_result .catalog # Get catalog message
318+ assert catalog_message .catalog is not None , "Catalog message missing catalog."
319+ discovered_catalog : AirbyteCatalog = catalog_message .catalog
324320 if not discovered_catalog .streams :
325321 raise ValueError (
326322 f"Discovered catalog for connector '{ connector_name } ' is empty. "
@@ -355,7 +351,7 @@ def test_docker_image_build_and_read(
355351 configured_catalog_path .write_text (
356352 orjson .dumps (asdict (configured_catalog )).decode ("utf-8" )
357353 )
358- read_result : CompletedProcess [ str ] = run_docker_command (
354+ read_result : EntrypointOutput = run_docker_airbyte_command (
359355 [
360356 "docker" ,
361357 "run" ,
@@ -371,18 +367,5 @@ def test_docker_image_build_and_read(
371367 "--catalog" ,
372368 container_catalog_path ,
373369 ],
374- check = False ,
375- capture_stderr = True ,
376- capture_stdout = True ,
370+ raise_if_errors = True ,
377371 )
378- if read_result .returncode != 0 :
379- raise AssertionError (
380- f"Failed to run `read` command in docker image { connector_image !r} . "
381- "\n -----------------"
382- f"EXIT CODE: { read_result .returncode } \n "
383- "STDERR:\n "
384- f"{ read_result .stderr } \n "
385- f"STDOUT:\n "
386- f"{ read_result .stdout } \n "
387- "\n -----------------"
388- ) from None
0 commit comments