Skip to content

Commit 10271de

Browse files
committed
fix imports / relocate PytestStep to tests/python_connectors.py
1 parent a6032b0 commit 10271de

File tree

2 files changed

+105
-103
lines changed

2 files changed

+105
-103
lines changed

airbyte-ci/connectors/pipelines/pipelines/bases.py

+3-101
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from dataclasses import dataclass, field
1414
from datetime import datetime, timedelta
1515
from enum import Enum
16-
from typing import TYPE_CHECKING, Any, Callable, ClassVar, Iterable, List, Optional, Set
16+
from typing import TYPE_CHECKING, Any, ClassVar, List, Optional, Set
1717

1818
import anyio
1919
import asyncer
@@ -22,8 +22,8 @@
2222
from dagger import Container, DaggerError
2323
from jinja2 import Environment, PackageLoader, select_autoescape
2424
from pipelines import sentry_utils
25-
from pipelines.actions import environments, remote_storage
26-
from pipelines.consts import GCS_PUBLIC_DOMAIN, LOCAL_REPORTS_PATH_ROOT, PYPROJECT_TOML_FILE_PATH
25+
from pipelines.actions import remote_storage
26+
from pipelines.consts import GCS_PUBLIC_DOMAIN, LOCAL_REPORTS_PATH_ROOT
2727
from pipelines.utils import METADATA_FILE_NAME, format_duration, get_exec_result
2828
from rich.console import Group
2929
from rich.panel import Panel
@@ -276,104 +276,6 @@ def _get_timed_out_step_result(self) -> StepResult:
276276
)
277277

278278

279-
class PytestStep(Step, ABC):
280-
"""An abstract class to run pytest tests and evaluate success or failure according to pytest logs."""
281-
282-
PYTEST_INI_FILE_NAME = "pytest.ini"
283-
PYPROJECT_FILE_NAME = "pyproject.toml"
284-
extra_dependencies_names = ("dev", "tests")
285-
skipped_exit_code = 5
286-
287-
@property
288-
@abstractmethod
289-
def test_directory_name(self) -> str:
290-
raise NotImplementedError("test_directory_name must be implemented in the child class.")
291-
292-
async def _run(self, connector_under_test: Container) -> StepResult:
293-
"""Run all pytest tests declared in the test directory of the connector code.
294-
295-
Args:
296-
connector_under_test (Container): The connector under test container.
297-
298-
Returns:
299-
StepResult: Failure or success of the unit tests with stdout and stdout.
300-
"""
301-
if not await self.check_if_tests_are_available(self.test_directory_name):
302-
return self.skip(f"No {self.test_directory_name} directory found in the connector.")
303-
304-
connector_under_test = connector_under_test.with_(await self.testing_environment(self.extra_dependencies_names))
305-
306-
return await self.get_step_result(connector_under_test)
307-
308-
async def check_if_tests_are_available(self, test_directory_name: str) -> bool:
309-
"""Check if the tests are available in the connector directory.
310-
311-
Returns:
312-
bool: True if the tests are available.
313-
"""
314-
connector_dir = await self.context.get_connector_dir()
315-
connector_dir_entries = await connector_dir.entries()
316-
return test_directory_name in connector_dir_entries
317-
318-
async def testing_environment(self, extra_dependencies_names: Iterable[str]) -> Callable:
319-
"""Install all extra dependencies of a connector.
320-
321-
Args:
322-
extra_dependencies_names (Iterable[str]): Extra dependencies to install.
323-
324-
Returns:
325-
Callable: The decorator to use with the with_ method of a container.
326-
"""
327-
secret_mounting_function = await environments.mounted_connector_secrets(self.context, "secrets")
328-
connector_dir = await self.context.get_connector_dir()
329-
connector_dir_entries = await connector_dir.entries()
330-
331-
if self.PYTEST_INI_FILE_NAME in connector_dir_entries:
332-
config_file_name = self.PYTEST_INI_FILE_NAME
333-
test_config = await self.context.get_connector_dir(include=[self.PYTEST_INI_FILE_NAME]).file(self.PYTEST_INI_FILE_NAME)
334-
self.logger.info(f"Found {self.PYTEST_INI_FILE_NAME}, using it for testing.")
335-
elif self.PYPROJECT_FILE_NAME in connector_dir_entries:
336-
config_file_name = self.PYPROJECT_FILE_NAME
337-
test_config = await self.context.get_connector_dir(include=[self.PYTEST_INI_FILE_NAME]).file(self.PYTEST_INI_FILE_NAME)
338-
self.logger.info(f"Found {PYPROJECT_TOML_FILE_PATH} at connector level, using it for testing.")
339-
else:
340-
config_file_name = f"global_{self.PYPROJECT_FILE_NAME}"
341-
test_config = await self.context.get_repo_dir(include=[self.PYPROJECT_FILE_NAME]).file(self.PYPROJECT_FILE_NAME)
342-
self.logger.info(f"Found {PYPROJECT_TOML_FILE_PATH} at repo level, using it for testing.")
343-
344-
def prepare_for_testing(built_connector_container: Container) -> Container:
345-
return (
346-
built_connector_container
347-
# Reset the entrypoint
348-
.with_entrypoint([])
349-
# Mount the connector directory in /test_environment
350-
# For build optimization the full directory is not mounted by default
351-
# We need the setup.py/pyproject.toml and the tests code to be available
352-
# Install the extra dependencies
353-
.with_mounted_directory("/test_environment", connector_dir)
354-
# Jump in the /test_environment directory
355-
.with_workdir("/test_environment").with_mounted_file(config_file_name, test_config)
356-
# Mount the secrets
357-
.with_(secret_mounting_function)
358-
# Install the extra dependencies
359-
.with_exec(["pip", "install", f".[{','.join(extra_dependencies_names)}]"], skip_entrypoint=True)
360-
# Execute pytest on the test directory
361-
.with_exec(
362-
[
363-
"python",
364-
"-m",
365-
"pytest",
366-
"-s",
367-
self.test_directory_name,
368-
"-c",
369-
config_file_name,
370-
]
371-
)
372-
)
373-
374-
return prepare_for_testing
375-
376-
377279
class NoOpStep(Step):
378280
"""A step that does nothing."""
379281

airbyte-ci/connectors/pipelines/pipelines/tests/python_connectors.py

+102-2
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@
44

55
"""This module groups steps made to run tests for a specific Python connector given a test context."""
66

7-
from typing import List
7+
from abc import ABC, abstractmethod
8+
from typing import Callable, Iterable, List
89

910
import asyncer
11+
from dagger import Container
1012
from pipelines.actions import environments, secrets
1113
from pipelines.bases import Step, StepResult, StepStatus
1214
from pipelines.builds import LOCAL_BUILD_PLATFORM
1315
from pipelines.builds.python_connectors import BuildConnectorImage
1416
from pipelines.contexts import ConnectorContext
15-
from pipelines.tests.common import AcceptanceTests, PytestStep
17+
from pipelines.tests.common import AcceptanceTests
1618
from pipelines.utils import export_container_to_tarball
1719

1820

@@ -52,6 +54,104 @@ async def _run(self) -> StepResult:
5254
return await self.get_step_result(formatter)
5355

5456

57+
class PytestStep(Step, ABC):
58+
"""An abstract class to run pytest tests and evaluate success or failure according to pytest logs."""
59+
60+
PYTEST_INI_FILE_NAME = "pytest.ini"
61+
PYPROJECT_FILE_NAME = "pyproject.toml"
62+
extra_dependencies_names = ("dev", "tests")
63+
skipped_exit_code = 5
64+
65+
@property
66+
@abstractmethod
67+
def test_directory_name(self) -> str:
68+
raise NotImplementedError("test_directory_name must be implemented in the child class.")
69+
70+
async def _run(self, connector_under_test: Container) -> StepResult:
71+
"""Run all pytest tests declared in the test directory of the connector code.
72+
73+
Args:
74+
connector_under_test (Container): The connector under test container.
75+
76+
Returns:
77+
StepResult: Failure or success of the unit tests with stdout and stdout.
78+
"""
79+
if not await self.check_if_tests_are_available(self.test_directory_name):
80+
return self.skip(f"No {self.test_directory_name} directory found in the connector.")
81+
82+
connector_under_test = connector_under_test.with_(await self.testing_environment(self.extra_dependencies_names))
83+
84+
return await self.get_step_result(connector_under_test)
85+
86+
async def check_if_tests_are_available(self, test_directory_name: str) -> bool:
87+
"""Check if the tests are available in the connector directory.
88+
89+
Returns:
90+
bool: True if the tests are available.
91+
"""
92+
connector_dir = await self.context.get_connector_dir()
93+
connector_dir_entries = await connector_dir.entries()
94+
return test_directory_name in connector_dir_entries
95+
96+
async def testing_environment(self, extra_dependencies_names: Iterable[str]) -> Callable:
97+
"""Install all extra dependencies of a connector.
98+
99+
Args:
100+
extra_dependencies_names (Iterable[str]): Extra dependencies to install.
101+
102+
Returns:
103+
Callable: The decorator to use with the with_ method of a container.
104+
"""
105+
secret_mounting_function = await environments.mounted_connector_secrets(self.context, "secrets")
106+
connector_dir = await self.context.get_connector_dir()
107+
connector_dir_entries = await connector_dir.entries()
108+
109+
if self.PYTEST_INI_FILE_NAME in connector_dir_entries:
110+
config_file_name = self.PYTEST_INI_FILE_NAME
111+
test_config = await self.context.get_connector_dir(include=[self.PYTEST_INI_FILE_NAME]).file(self.PYTEST_INI_FILE_NAME)
112+
self.logger.info(f"Found {self.PYTEST_INI_FILE_NAME}, using it for testing.")
113+
elif self.PYPROJECT_FILE_NAME in connector_dir_entries:
114+
config_file_name = self.PYPROJECT_FILE_NAME
115+
test_config = await self.context.get_connector_dir(include=[self.PYTEST_INI_FILE_NAME]).file(self.PYTEST_INI_FILE_NAME)
116+
self.logger.info(f"Found {self.PYPROJECT_FILE_NAME} at connector level, using it for testing.")
117+
else:
118+
config_file_name = f"global_{self.PYPROJECT_FILE_NAME}"
119+
test_config = await self.context.get_repo_dir(include=[self.PYPROJECT_FILE_NAME]).file(self.PYPROJECT_FILE_NAME)
120+
self.logger.info(f"Found {self.PYPROJECT_FILE_NAME} at repo level, using it for testing.")
121+
122+
def prepare_for_testing(built_connector_container: Container) -> Container:
123+
return (
124+
built_connector_container
125+
# Reset the entrypoint
126+
.with_entrypoint([])
127+
# Mount the connector directory in /test_environment
128+
# For build optimization the full directory is not mounted by default
129+
# We need the setup.py/pyproject.toml and the tests code to be available
130+
# Install the extra dependencies
131+
.with_mounted_directory("/test_environment", connector_dir)
132+
# Jump in the /test_environment directory
133+
.with_workdir("/test_environment").with_mounted_file(config_file_name, test_config)
134+
# Mount the secrets
135+
.with_(secret_mounting_function)
136+
# Install the extra dependencies
137+
.with_exec(["pip", "install", f".[{','.join(extra_dependencies_names)}]"], skip_entrypoint=True)
138+
# Execute pytest on the test directory
139+
.with_exec(
140+
[
141+
"python",
142+
"-m",
143+
"pytest",
144+
"-s",
145+
self.test_directory_name,
146+
"-c",
147+
config_file_name,
148+
]
149+
)
150+
)
151+
152+
return prepare_for_testing
153+
154+
55155
class UnitTests(PytestStep):
56156
"""A step to run the connector unit tests with Pytest."""
57157

0 commit comments

Comments
 (0)