|
3 | 3 |
|
4 | 4 | from __future__ import annotations |
5 | 5 |
|
6 | | -import abc |
7 | 6 | import importlib |
8 | | -import inspect |
9 | 7 | import os |
10 | | -import shutil |
11 | | -import sys |
12 | | -from collections.abc import Callable |
13 | 8 | from pathlib import Path |
14 | | -from typing import cast |
| 9 | +from typing import TYPE_CHECKING, cast |
15 | 10 |
|
16 | | -import pytest |
17 | | -import yaml |
18 | 11 | from boltons.typeutils import classproperty |
19 | 12 |
|
20 | 13 | from airbyte_cdk.models import ( |
21 | 14 | AirbyteMessage, |
22 | 15 | Type, |
23 | 16 | ) |
24 | | -from airbyte_cdk.models.connector_metadata import MetadataFile |
25 | | -from airbyte_cdk.test import entrypoint_wrapper |
26 | 17 | from airbyte_cdk.test.standard_tests._job_runner import IConnector, run_test_job |
27 | | -from airbyte_cdk.test.standard_tests.models import ( |
28 | | - ConnectorTestScenario, |
29 | | -) |
30 | | -from airbyte_cdk.utils.connector_paths import ( |
31 | | - ACCEPTANCE_TEST_CONFIG, |
32 | | - find_connector_root, |
33 | | -) |
34 | | -from airbyte_cdk.utils.docker import build_connector_image, run_docker_command |
| 18 | +from airbyte_cdk.test.standard_tests.docker_base import DockerConnectorTestSuite |
| 19 | + |
| 20 | +if TYPE_CHECKING: |
| 21 | + from collections.abc import Callable |
| 22 | + |
| 23 | + from airbyte_cdk.test import entrypoint_wrapper |
| 24 | + from airbyte_cdk.test.standard_tests.models import ( |
| 25 | + ConnectorTestScenario, |
| 26 | + ) |
35 | 27 |
|
36 | 28 |
|
37 | | -class ConnectorTestSuiteBase(abc.ABC): |
38 | | - """Base class for connector test suites.""" |
| 29 | +class ConnectorTestSuiteBase(DockerConnectorTestSuite): |
| 30 | + """Base class for Python connector test suites.""" |
39 | 31 |
|
40 | 32 | connector: type[IConnector] | Callable[[], IConnector] | None # type: ignore [reportRedeclaration] |
41 | 33 | """The connector class or a factory function that returns an scenario of IConnector.""" |
@@ -83,13 +75,6 @@ def connector(cls) -> type[IConnector] | Callable[[], IConnector] | None: |
83 | 75 | ) from e |
84 | 76 | return cast(type[IConnector], getattr(module, matching_class_name)) |
85 | 77 |
|
86 | | - @classmethod |
87 | | - def get_test_class_dir(cls) -> Path: |
88 | | - """Get the file path that contains the class.""" |
89 | | - module = sys.modules[cls.__module__] |
90 | | - # Get the directory containing the test file |
91 | | - return Path(inspect.getfile(module)).parent |
92 | | - |
93 | 78 | @classmethod |
94 | 79 | def create_connector( |
95 | 80 | cls, |
@@ -127,148 +112,3 @@ def test_check( |
127 | 112 | assert len(conn_status_messages) == 1, ( |
128 | 113 | f"Expected exactly one CONNECTION_STATUS message. Got: {result._messages}" |
129 | 114 | ) |
130 | | - |
131 | | - @classmethod |
132 | | - def get_connector_root_dir(cls) -> Path: |
133 | | - """Get the root directory of the connector.""" |
134 | | - return find_connector_root([cls.get_test_class_dir(), Path.cwd()]) |
135 | | - |
136 | | - @classproperty |
137 | | - def acceptance_test_config_path(cls) -> Path: |
138 | | - """Get the path to the acceptance test config file.""" |
139 | | - result = cls.get_connector_root_dir() / ACCEPTANCE_TEST_CONFIG |
140 | | - if result.exists(): |
141 | | - return result |
142 | | - |
143 | | - raise FileNotFoundError(f"Acceptance test config file not found at: {str(result)}") |
144 | | - |
145 | | - @classmethod |
146 | | - def get_scenarios( |
147 | | - cls, |
148 | | - ) -> list[ConnectorTestScenario]: |
149 | | - """Get acceptance tests for a given category. |
150 | | -
|
151 | | - This has to be a separate function because pytest does not allow |
152 | | - parametrization of fixtures with arguments from the test class itself. |
153 | | - """ |
154 | | - categories = ["connection", "spec"] |
155 | | - all_tests_config = yaml.safe_load(cls.acceptance_test_config_path.read_text()) |
156 | | - if "acceptance_tests" not in all_tests_config: |
157 | | - raise ValueError( |
158 | | - f"Acceptance tests config not found in {cls.acceptance_test_config_path}." |
159 | | - f" Found only: {str(all_tests_config)}." |
160 | | - ) |
161 | | - |
162 | | - test_scenarios: list[ConnectorTestScenario] = [] |
163 | | - for category in categories: |
164 | | - if ( |
165 | | - category not in all_tests_config["acceptance_tests"] |
166 | | - or "tests" not in all_tests_config["acceptance_tests"][category] |
167 | | - ): |
168 | | - continue |
169 | | - |
170 | | - test_scenarios.extend( |
171 | | - [ |
172 | | - ConnectorTestScenario.model_validate(test) |
173 | | - for test in all_tests_config["acceptance_tests"][category]["tests"] |
174 | | - if "config_path" in test and "iam_role" not in test["config_path"] |
175 | | - ] |
176 | | - ) |
177 | | - |
178 | | - connector_root = cls.get_connector_root_dir().absolute() |
179 | | - for test in test_scenarios: |
180 | | - if test.config_path: |
181 | | - test.config_path = connector_root / test.config_path |
182 | | - if test.configured_catalog_path: |
183 | | - test.configured_catalog_path = connector_root / test.configured_catalog_path |
184 | | - |
185 | | - return test_scenarios |
186 | | - |
187 | | - @pytest.mark.skipif( |
188 | | - shutil.which("docker") is None, |
189 | | - reason="docker CLI not found in PATH, skipping docker image tests", |
190 | | - ) |
191 | | - @pytest.mark.image_tests |
192 | | - def test_docker_image_build_and_spec( |
193 | | - self, |
194 | | - connector_image_override: str | None, |
195 | | - ) -> None: |
196 | | - """Run `docker_image` acceptance tests.""" |
197 | | - connector_dir = self.get_connector_root_dir() |
198 | | - metadata = MetadataFile.from_file(connector_dir / "metadata.yaml") |
199 | | - |
200 | | - connector_image: str | None = connector_image_override |
201 | | - if not connector_image: |
202 | | - tag = "dev-latest" |
203 | | - connector_image = build_connector_image( |
204 | | - connector_name=connector_dir.name, |
205 | | - connector_directory=connector_dir, |
206 | | - metadata=metadata, |
207 | | - tag=tag, |
208 | | - no_verify=False, |
209 | | - ) |
210 | | - |
211 | | - _ = run_docker_command( |
212 | | - [ |
213 | | - "docker", |
214 | | - "run", |
215 | | - "--rm", |
216 | | - connector_image, |
217 | | - "spec", |
218 | | - ], |
219 | | - check=True, # Raise an error if the command fails |
220 | | - capture_output=False, |
221 | | - ) |
222 | | - |
223 | | - @pytest.mark.skipif( |
224 | | - shutil.which("docker") is None, |
225 | | - reason="docker CLI not found in PATH, skipping docker image tests", |
226 | | - ) |
227 | | - @pytest.mark.image_tests |
228 | | - def test_docker_image_build_and_check( |
229 | | - self, |
230 | | - scenario: ConnectorTestScenario, |
231 | | - connector_image_override: str | None, |
232 | | - ) -> None: |
233 | | - """Run `docker_image` acceptance tests. |
234 | | -
|
235 | | - This test builds the connector image and runs the `check` command inside the container. |
236 | | -
|
237 | | - Note: |
238 | | - - It is expected for docker image caches to be reused between test runs. |
239 | | - - In the rare case that image caches need to be cleared, please clear |
240 | | - the local docker image cache using `docker image prune -a` command. |
241 | | - """ |
242 | | - if scenario.expect_exception: |
243 | | - pytest.skip("Skipping test_docker_image_build_and_check (expected to fail).") |
244 | | - |
245 | | - tag = "dev-latest" |
246 | | - connector_dir = self.get_connector_root_dir() |
247 | | - metadata = MetadataFile.from_file(connector_dir / "metadata.yaml") |
248 | | - connector_image: str | None = connector_image_override |
249 | | - if not connector_image: |
250 | | - tag = "dev-latest" |
251 | | - connector_image = build_connector_image( |
252 | | - connector_name=connector_dir.name, |
253 | | - connector_directory=connector_dir, |
254 | | - metadata=metadata, |
255 | | - tag=tag, |
256 | | - no_verify=False, |
257 | | - ) |
258 | | - |
259 | | - container_config_path = "/secrets/config.json" |
260 | | - with scenario.with_temp_config_file() as temp_config_file: |
261 | | - _ = run_docker_command( |
262 | | - [ |
263 | | - "docker", |
264 | | - "run", |
265 | | - "--rm", |
266 | | - "-v", |
267 | | - f"{temp_config_file}:{container_config_path}", |
268 | | - connector_image, |
269 | | - "check", |
270 | | - f"--config={container_config_path}", |
271 | | - ], |
272 | | - check=True, # Raise an error if the command fails |
273 | | - capture_output=False, |
274 | | - ) |
0 commit comments