-
Notifications
You must be signed in to change notification settings - Fork 83
feat(integration-tests): Spin up and tear down the clp-text and clp-json packages.
#1437
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 52 commits
8e75e15
44ee20e
bbe0575
5cd47f8
c5375eb
2be837f
bf14b5e
27b7f5b
e041f0e
75672fc
0822a79
2df9515
63f3ae4
0011447
0ff9465
cdcfaee
53e7343
796ffe3
bae12de
9c9ef74
0f3dcd5
f9865a8
865e9ed
09d5200
5fffc32
571f313
e348be0
30f186f
e5da7e9
292ec70
0c4cfa4
bee738a
49eaf97
e3c4a3b
4cd92e6
87c4809
baddcfe
7cf597d
8372a9f
b92ee22
f950df0
aac6fcb
9f995b9
03cb974
0e0567a
d44ab95
324ade4
e7e79e1
7116cfc
3471168
a324248
6caf53a
f3e533d
a71bcb0
b5cf248
b496025
86ab692
ce822d8
8224985
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,9 @@ | ||
| """Make the fixtures defined in `tests/fixtures/` globally available without imports.""" | ||
| """Global pytest setup.""" | ||
|
|
||
| # Make the fixtures defined in `tests/fixtures/` globally available without imports. | ||
| pytest_plugins = [ | ||
| "tests.fixtures.integration_test_logs", | ||
| "tests.fixtures.path_configs", | ||
| "tests.fixtures.package_instance", | ||
| "tests.fixtures.package_config", | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| """Fixtures that create and remove temporary config files for CLP packages.""" | ||
quinntaylormitchell marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| import contextlib | ||
| from collections.abc import Iterator | ||
|
|
||
| import pytest | ||
|
|
||
| from tests.utils.clp_mode_utils import ( | ||
| get_clp_config_from_mode, | ||
| ) | ||
quinntaylormitchell marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| from tests.utils.config import PackageConfig, PackagePathConfig | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def fixt_package_config( | ||
| fixt_package_path_config: PackagePathConfig, | ||
| request: pytest.FixtureRequest, | ||
quinntaylormitchell marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ) -> Iterator[PackageConfig]: | ||
| """ | ||
| Creates and maintains a PackageConfig object for a specific CLP mode. | ||
| :param request: | ||
| :return: An iterator that yields the PackageConfig object for the specified mode. | ||
| """ | ||
| mode_name: str = request.param | ||
|
|
||
| # Get the ClpConfig for this mode. | ||
| clp_config_obj = get_clp_config_from_mode(mode_name) | ||
|
|
||
| # Construct PackageConfig. | ||
| package_config = PackageConfig( | ||
| path_config=fixt_package_path_config, | ||
| mode_name=mode_name, | ||
| clp_config=clp_config_obj, | ||
| ) | ||
|
|
||
| try: | ||
| yield package_config | ||
| finally: | ||
| with contextlib.suppress(FileNotFoundError): | ||
Bill-hbrhbr marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| package_config.temp_config_file_path.unlink() | ||
quinntaylormitchell marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| """Fixtures that start and stop CLP package instances for integration tests.""" | ||
quinntaylormitchell marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| import subprocess | ||
| from collections.abc import Iterator | ||
|
|
||
| import pytest | ||
|
|
||
| from tests.utils.config import ( | ||
| PackageConfig, | ||
| PackageInstance, | ||
| ) | ||
| from tests.utils.package_utils import ( | ||
| start_clp_package, | ||
| stop_clp_package, | ||
| ) | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def fixt_package_instance(fixt_package_config: PackageConfig) -> Iterator[PackageInstance]: | ||
| """ | ||
| Starts a CLP package instance for the given configuration and stops it during teardown. | ||
| :param fixt_package_config: | ||
| :param request: | ||
quinntaylormitchell marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| :return: Iterator that yields the running package instance. | ||
| """ | ||
| mode_name = fixt_package_config.mode_name | ||
| instance: PackageInstance | None = None | ||
quinntaylormitchell marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| try: | ||
| start_clp_package(fixt_package_config) | ||
| instance = PackageInstance(package_config=fixt_package_config) | ||
| yield instance | ||
| except RuntimeError: | ||
| pytest.fail(f"Failed to start the {mode_name} package.") | ||
| finally: | ||
| if instance is not None: | ||
| stop_clp_package(instance) | ||
| else: | ||
| # This means setup failed after start; fall back to calling stop script directly | ||
| subprocess.run([str(fixt_package_config.path_config.stop_script_path)], check=False) | ||
Bill-hbrhbr marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
quinntaylormitchell marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| """Integration tests verifying that the CLP package can be started and stopped.""" | ||
|
|
||
| import logging | ||
|
|
||
| import pytest | ||
|
|
||
| from tests.utils.clp_mode_utils import CLP_MODE_CONFIGS | ||
| from tests.utils.config import PackageInstance | ||
|
|
||
| TEST_MODES = CLP_MODE_CONFIGS.keys() | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
|
|
||
| @pytest.mark.package | ||
| @pytest.mark.parametrize("fixt_package_config", TEST_MODES, indirect=True) | ||
| def test_clp_package(fixt_package_instance: PackageInstance) -> None: | ||
| """ | ||
| Validate that the CLP package starts up successfully for the selected mode of operation. | ||
| :param fixt_package_instance: | ||
| """ | ||
| # TODO: write code that properly validates that the package is running. This is a placeholder. | ||
| mode_name = fixt_package_instance.package_config.mode_name | ||
| message = f"The '{mode_name}' package has been spun up successfully." | ||
| logger.info(message) | ||
Bill-hbrhbr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| """Provides utilities related to the user-level configurations of CLP's operating modes.""" | ||
|
|
||
| from collections.abc import Callable | ||
|
|
||
| from clp_py_utils.clp_config import ( | ||
| ClpConfig, | ||
| Package, | ||
| QueryEngine, | ||
| StorageEngine, | ||
| ) | ||
|
|
||
| CLP_MODE_CONFIGS: dict[str, Callable[[], ClpConfig]] = { | ||
| "clp-text": lambda: ClpConfig( | ||
| package=Package( | ||
| storage_engine=StorageEngine.CLP, | ||
| query_engine=QueryEngine.CLP, | ||
| ), | ||
| ), | ||
| "clp-json": lambda: ClpConfig( | ||
| package=Package( | ||
| storage_engine=StorageEngine.CLP_S, | ||
| query_engine=QueryEngine.CLP_S, | ||
| ), | ||
| ), | ||
| } | ||
|
|
||
|
|
||
| def get_clp_config_from_mode(mode_name: str) -> ClpConfig: | ||
| """ | ||
| Return a ClpConfig object for the given mode name. | ||
|
|
||
| :param mode_name: | ||
| :return: ClpConfig object corresponding to the mode. | ||
| :raise ValueError: If the mode is not supported. | ||
| """ | ||
| try: | ||
| config = CLP_MODE_CONFIGS[mode_name] | ||
| except KeyError as err: | ||
| err_msg = f"Unsupported mode: {mode_name}" | ||
| raise ValueError(err_msg) from err | ||
| return config() |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -4,12 +4,19 @@ | |||||||||
|
|
||||||||||
| from dataclasses import dataclass, field, InitVar | ||||||||||
| from pathlib import Path | ||||||||||
| from typing import TYPE_CHECKING | ||||||||||
quinntaylormitchell marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
|
|
||||||||||
| import yaml | ||||||||||
|
|
||||||||||
| from tests.utils.utils import ( | ||||||||||
| unlink, | ||||||||||
| validate_dir_exists, | ||||||||||
| validate_file_exists, | ||||||||||
| ) | ||||||||||
|
|
||||||||||
| if TYPE_CHECKING: | ||||||||||
| from clp_py_utils.clp_config import ClpConfig | ||||||||||
Bill-hbrhbr marked this conversation as resolved.
Show resolved
Hide resolved
quinntaylormitchell marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
|
|
||||||||||
|
|
||||||||||
| @dataclass(frozen=True) | ||||||||||
| class ClpCorePathConfig: | ||||||||||
|
|
@@ -61,8 +68,15 @@ class PackagePathConfig: | |||||||||
| #: Root directory containing all CLP package contents. | ||||||||||
| clp_package_dir: Path | ||||||||||
|
|
||||||||||
| def __post_init__(self) -> None: | ||||||||||
| """Validates that the CLP package directory exists and contains all required directories.""" | ||||||||||
| #: Root directory for package tests output. | ||||||||||
| test_root_dir: InitVar[Path] | ||||||||||
|
|
||||||||||
| #: Directory to store any cached package config files. | ||||||||||
| temp_config_dir: Path = field(init=False, repr=True) | ||||||||||
|
|
||||||||||
| def __post_init__(self, test_root_dir: Path) -> None: | ||||||||||
| """Validates init values and initializes attributes.""" | ||||||||||
| # Validate that the CLP package directory exists and contains required directories. | ||||||||||
| clp_package_dir = self.clp_package_dir | ||||||||||
| validate_dir_exists(clp_package_dir) | ||||||||||
|
|
||||||||||
|
|
@@ -75,6 +89,70 @@ def __post_init__(self) -> None: | |||||||||
| ) | ||||||||||
| raise RuntimeError(err_msg) | ||||||||||
|
|
||||||||||
| # Initialize cache directory for package tests. | ||||||||||
| validate_dir_exists(test_root_dir) | ||||||||||
| object.__setattr__(self, "temp_config_dir", test_root_dir / "temp_config_files") | ||||||||||
|
|
||||||||||
| # Create directories if they do not already exist. | ||||||||||
| self.temp_config_dir.mkdir(parents=True, exist_ok=True) | ||||||||||
|
|
||||||||||
| @property | ||||||||||
| def start_script_path(self) -> Path: | ||||||||||
| """:return: The absolute path to the package start script.""" | ||||||||||
| return self.clp_package_dir / "sbin" / "start-clp.sh" | ||||||||||
|
|
||||||||||
| @property | ||||||||||
| def stop_script_path(self) -> Path: | ||||||||||
| """:return: The absolute path to the package stop script.""" | ||||||||||
| return self.clp_package_dir / "sbin" / "stop-clp.sh" | ||||||||||
|
|
||||||||||
|
|
||||||||||
| @dataclass(frozen=True) | ||||||||||
| class PackageConfig: | ||||||||||
| """Metadata for a specific configuration of the CLP package.""" | ||||||||||
|
|
||||||||||
| #: Path configuration for this package. | ||||||||||
| path_config: PackagePathConfig | ||||||||||
|
|
||||||||||
| #: Name of the mode of operation represented in this config. | ||||||||||
quinntaylormitchell marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||
| mode_name: str | ||||||||||
|
|
||||||||||
| #: The ClpConfig instance that describes this package configuration. | ||||||||||
|
||||||||||
| #: The ClpConfig instance that describes this package configuration. | |
| #: The Pydantic representation of a `clp-config.yaml`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mostly agreed; I'm going to do
| #: The ClpConfig instance that describes this package configuration. | |
| #: The Pydantic representation of a CLP package configuration. |
because it isn't strictly representing a .yaml file (in fact, it seems like the .yaml file is representing the ClpConfig object, not the other way around)
quinntaylormitchell marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| """Provides utility functions related to the CLP package used across `integration-tests`.""" | ||
|
|
||
| import subprocess | ||
|
|
||
| from tests.utils.config import ( | ||
| PackageConfig, | ||
| PackageInstance, | ||
| ) | ||
|
|
||
|
|
||
| def start_clp_package(package_config: PackageConfig) -> None: | ||
| """ | ||
| Starts an instance of the CLP package. | ||
| :param package_config: | ||
| :raise RuntimeError: If the package fails to start. | ||
| """ | ||
| path_config = package_config.path_config | ||
| start_script_path = path_config.start_script_path | ||
| temp_config_file_path = package_config.temp_config_file_path | ||
| try: | ||
| # fmt: off | ||
| start_cmd = [ | ||
| str(start_script_path), | ||
| "--config", str(temp_config_file_path), | ||
| ] | ||
| # fmt: on | ||
| subprocess.run(start_cmd, check=True) | ||
| except Exception as err: | ||
| err_msg = f"Failed to start an instance of the {package_config.mode_name} package." | ||
| raise RuntimeError(err_msg) from err | ||
|
|
||
|
|
||
| def stop_clp_package(instance: PackageInstance) -> None: | ||
| """ | ||
| Stops an instance of the CLP package. | ||
| :param instance: | ||
| :raise RuntimeError: If the package fails to stop. | ||
| """ | ||
quinntaylormitchell marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| package_config = instance.package_config | ||
| path_config = package_config.path_config | ||
| stop_script_path = path_config.stop_script_path | ||
| try: | ||
| # fmt: off | ||
| stop_cmd = [ | ||
| str(stop_script_path) | ||
| ] | ||
quinntaylormitchell marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| # fmt: on | ||
| subprocess.run(stop_cmd, check=True) | ||
| except Exception as err: | ||
| err_msg = f"Failed to stop an instance of the {package_config.mode_name} package." | ||
quinntaylormitchell marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| raise RuntimeError(err_msg) from err | ||
Uh oh!
There was an error while loading. Please reload this page.