88import inspect
99import os
1010import sys
11- from collections .abc import Callable
1211from pathlib import Path
13- from typing import cast
12+ from typing import Literal , cast
1413
14+ import pytest
1515import yaml
1616from boltons .typeutils import classproperty
1717
2121)
2222from airbyte_cdk .test import entrypoint_wrapper
2323from airbyte_cdk .test .standard_tests ._job_runner import IConnector , run_test_job
24+ from airbyte_cdk .test .standard_tests .docker_connectors import DockerConnector
2425from airbyte_cdk .test .standard_tests .models import (
2526 ConnectorTestScenario ,
2627)
3031)
3132
3233
34+ @pytest .fixture
35+ def use_docker_image (request : pytest .FixtureRequest ) -> str | bool :
36+ """Fixture to determine if a Docker image should be used for the test."""
37+ return request .config .getoption ("use_docker_image" )
38+
39+
3340class ConnectorTestSuiteBase (abc .ABC ):
3441 """Base class for connector test suites."""
3542
36- connector : type [IConnector ] | Callable [[], IConnector ] | None # type: ignore [reportRedeclaration]
43+ connector_class : type [IConnector ] | None = None
3744 """The connector class or a factory function that returns an scenario of IConnector."""
3845
39- @classproperty # type: ignore [no-redef]
40- def connector (cls ) -> type [IConnector ] | Callable [[], IConnector ] | None :
46+ @classmethod
47+ def get_test_class_dir (cls ) -> Path :
48+ """Get the file path that contains the class."""
49+ module = sys .modules [cls .__module__ ]
50+ # Get the directory containing the test file
51+ return Path (inspect .getfile (module )).parent
52+
53+ @classmethod
54+ def create_connector (
55+ cls ,
56+ scenario : ConnectorTestScenario ,
57+ use_docker_image : str | bool ,
58+ ) -> IConnector :
59+ """Instantiate the connector class."""
4160 """Get the connector class for the test suite.
4261
4362 This assumes a python connector and should be overridden by subclasses to provide the
4463 specific connector class to be tested.
4564 """
65+ if use_docker_image :
66+ return cls .create_docker_connector (
67+ docker_image = use_docker_image ,
68+ )
69+
4670 connector_root = cls .get_connector_root_dir ()
4771 connector_name = connector_root .absolute ().name
4872
@@ -65,7 +89,10 @@ def connector(cls) -> type[IConnector] | Callable[[], IConnector] | None:
6589
6690 # Dynamically get the class from the module
6791 try :
68- return cast (type [IConnector ], getattr (module , expected_class_name ))
92+ return cast (
93+ type [IConnector ],
94+ getattr (module , expected_class_name ),
95+ )()
6996 except AttributeError as e :
7097 # We did not find it based on our expectations, so let's check if we can find it
7198 # with a case-insensitive match.
@@ -77,21 +104,13 @@ def connector(cls) -> type[IConnector] | Callable[[], IConnector] | None:
77104 raise ImportError (
78105 f"Module '{ expected_module_name } ' does not have a class named '{ expected_class_name } '."
79106 ) from e
80- return cast (type [IConnector ], getattr (module , matching_class_name ))
107+ return cast (
108+ type [IConnector ],
109+ getattr (module , matching_class_name ),
110+ )()
81111
82- @classmethod
83- def get_test_class_dir (cls ) -> Path :
84- """Get the file path that contains the class."""
85- module = sys .modules [cls .__module__ ]
86- # Get the directory containing the test file
87- return Path (inspect .getfile (module )).parent
112+ _ = scenario
88113
89- @classmethod
90- def create_connector (
91- cls ,
92- scenario : ConnectorTestScenario ,
93- ) -> IConnector :
94- """Instantiate the connector class."""
95114 connector = cls .connector # type: ignore
96115 if connector :
97116 if callable (connector ) or isinstance (connector , type ):
@@ -105,15 +124,46 @@ def create_connector(
105124 "override `cls.create_connector()` to define a custom initialization process."
106125 )
107126
127+ @classmethod
128+ def create_docker_connector (
129+ cls ,
130+ docker_image : str | Literal [True ],
131+ ) -> IConnector :
132+ """Create a connector instance using Docker."""
133+ if not docker_image :
134+ raise ValueError ("Docker image is required to create a Docker connector." )
135+
136+ # Create the connector object by building the connector
137+ if docker_image is True :
138+ return DockerConnector .from_connector_directory (
139+ connector_directory = cls .get_connector_root_dir (),
140+ )
141+
142+ if not isinstance (docker_image , str ):
143+ raise ValueError (
144+ "Expected `docker_image` to be 'True' or of type `str`. "
145+ f"Type found: { type (docker_image ).__name__ } "
146+ )
147+
148+ # Create the connector object using the provided Docker image
149+ return DockerConnector (
150+ connector_name = cls .get_connector_root_dir ().name ,
151+ docker_image = docker_image ,
152+ )
153+
108154 # Test Definitions
109155
110156 def test_check (
111157 self ,
112158 scenario : ConnectorTestScenario ,
159+ use_docker_image : str | bool ,
113160 ) -> None :
114161 """Run `connection` acceptance tests."""
115162 result : entrypoint_wrapper .EntrypointOutput = run_test_job (
116- self .create_connector (scenario ),
163+ self .create_connector (
164+ scenario ,
165+ use_docker_image = use_docker_image ,
166+ ),
117167 "check" ,
118168 test_scenario = scenario ,
119169 )
0 commit comments