diff --git a/integration-tests/.pytest.ini b/integration-tests/.pytest.ini index 6124409099..9b9f60cc85 100644 --- a/integration-tests/.pytest.ini +++ b/integration-tests/.pytest.ini @@ -1,20 +1,27 @@ [pytest] addopts = - --capture=no --code-highlight=yes --color=yes - -rA + --show-capture=no --strict-config --strict-markers - --verbose + --tb=short + env = D:CLP_BUILD_DIR=../build D:CLP_CORE_BINS_DIR=../build/core D:CLP_PACKAGE_DIR=../build/clp-package + log_cli = True -log_cli_date_format = %Y-%m-%d %H:%M:%S,%f -log_cli_format = %(name)s %(asctime)s [%(levelname)s] %(message)s log_cli_level = INFO +log_cli_format = %(asctime)s.%(msecs)03d %(levelname)s %(message)s +log_cli_date_format = %Y-%m-%d %H:%M:%S + +log_file_mode = a +log_file_level = DEBUG +log_file_format = %(asctime)s.%(msecs)03d %(levelname)s [%(name)s:%(filename)s:%(lineno)d]: %(message)s +log_file_date_format = %Y-%m-%d %H:%M:%S + markers = clp: mark tests that use the CLP storage engine clp_json: mark tests that use the clp-json package diff --git a/integration-tests/pyproject.toml b/integration-tests/pyproject.toml index 120ea29a7c..b62632f3c2 100644 --- a/integration-tests/pyproject.toml +++ b/integration-tests/pyproject.toml @@ -43,6 +43,9 @@ show_error_end = true [tool.ruff] extend = "../tools/yscope-dev-utils/exports/lint-configs/python/ruff.toml" +[tool.ruff.lint] +ignore = ["TRY400"] + [tool.uv] default-groups = ["clp", "dev"] diff --git a/integration-tests/tests/conftest.py b/integration-tests/tests/conftest.py index 1741453b3a..9e5caf48d7 100644 --- a/integration-tests/tests/conftest.py +++ b/integration-tests/tests/conftest.py @@ -1,7 +1,14 @@ """Global pytest setup.""" +import datetime +from collections.abc import Iterator +from pathlib import Path + import pytest +from tests.utils.logging_utils import BLUE, BOLD, RESET +from tests.utils.utils import resolve_path_env_var + # Make the fixtures defined in `tests/fixtures/` globally available without imports. pytest_plugins = [ "tests.fixtures.integration_test_logs", @@ -11,9 +18,10 @@ ] +@pytest.hookimpl() def pytest_addoption(parser: pytest.Parser) -> None: """ - Adds options for pytest. + Adds options for `pytest`. :param parser: """ @@ -23,3 +31,58 @@ def pytest_addoption(parser: pytest.Parser) -> None: default="55000", help="Base port for CLP package integration tests.", ) + + # Sets up a unique log file for this test run, and stores the path to the file. + now = datetime.datetime.now() # noqa: DTZ005 + test_run_id = now.strftime("%Y-%m-%d-%H-%M-%S") + log_file_path = ( + resolve_path_env_var("CLP_BUILD_DIR") + / "integration-tests" + / "test_logs" + / f"testrun_{test_run_id}.log" + ) + parser.addini( + "log_file_path", + help="Path to the log file for this test.", + type="string", + default=str(log_file_path), + ) + + +def pytest_itemcollected(item: pytest.Item) -> None: + """ + Prettifies the name of the test for output purposes. + + :param item: + """ + item._nodeid = f"{BOLD}{BLUE}{item.nodeid}{RESET}" # noqa: SLF001 + + +@pytest.hookimpl(tryfirst=True) +def pytest_report_header(config: pytest.Config) -> str: + """ + Adds a field to the header at the start of the test run that reports the path to the log file + for this test run. + + :param config: + """ + log_file_path = Path(config.getini("log_file_path")).expanduser().resolve() + return f"Log file path for this test run: {log_file_path}" + + +@pytest.hookimpl(wrapper=True) +def pytest_runtest_setup(item: pytest.Item) -> Iterator[None]: + """ + Sets `log_file_path` as the output file for the logger. + + :param item: + """ + config = item.config + logging_plugin = config.pluginmanager.get_plugin("logging-plugin") + if logging_plugin is None: + err_msg = "Expected pytest plugin 'logging-plugin' to be registered." + raise RuntimeError(err_msg) + + log_file_path = Path(config.getini("log_file_path")) + logging_plugin.set_log_path(str(log_file_path)) + yield diff --git a/integration-tests/tests/fixtures/package_instance.py b/integration-tests/tests/fixtures/package_instance.py index c4425cdca5..f346aaccc8 100644 --- a/integration-tests/tests/fixtures/package_instance.py +++ b/integration-tests/tests/fixtures/package_instance.py @@ -1,5 +1,6 @@ """Fixtures that start and stop CLP package instances for integration tests.""" +import logging from collections.abc import Iterator import pytest @@ -13,9 +14,13 @@ stop_clp_package, ) +logger = logging.getLogger(__name__) + @pytest.fixture(scope="module") -def fixt_package_instance(fixt_package_test_config: PackageTestConfig) -> Iterator[PackageInstance]: +def fixt_package_instance( + request: pytest.FixtureRequest, fixt_package_test_config: PackageTestConfig +) -> Iterator[PackageInstance]: """ Starts a CLP package instance for the given configuration and stops it during teardown. @@ -25,18 +30,14 @@ def fixt_package_instance(fixt_package_test_config: PackageTestConfig) -> Iterat :param fixt_package_test_config: :return: Iterator that yields the running package instance. """ + mode_config = fixt_package_test_config.mode_config + mode_name = mode_config.mode_name + try: - start_clp_package(fixt_package_test_config) + logger.info("Starting the '%s' package...", mode_name) + start_clp_package(request, fixt_package_test_config) instance = PackageInstance(package_test_config=fixt_package_test_config) yield instance - except RuntimeError: - mode_config = fixt_package_test_config.mode_config - mode_name = mode_config.mode_name - base_port = fixt_package_test_config.base_port - pytest.fail( - f"Failed to start the {mode_name} package. This could mean that one of the ports" - f" derived from base_port={base_port} was unavailable. You can specify a new value for" - " base_port with the '--base-port' flag." - ) finally: - stop_clp_package(fixt_package_test_config) + logger.info("Stopping the '%s' package...", mode_name) + stop_clp_package(request, fixt_package_test_config) diff --git a/integration-tests/tests/fixtures/package_test_config.py b/integration-tests/tests/fixtures/package_test_config.py index e10ae7f3b9..fc0f1ccbb0 100644 --- a/integration-tests/tests/fixtures/package_test_config.py +++ b/integration-tests/tests/fixtures/package_test_config.py @@ -1,12 +1,16 @@ """Fixtures that create and remove temporary config files for CLP packages.""" +import logging from collections.abc import Iterator import pytest from tests.utils.config import PackageModeConfig, PackagePathConfig, PackageTestConfig +from tests.utils.logging_utils import construct_log_err_msg from tests.utils.port_utils import assign_ports_from_base +logger = logging.getLogger(__name__) + @pytest.fixture(scope="module") def fixt_package_test_config( @@ -22,14 +26,19 @@ def fixt_package_test_config( :raise ValueError: if the CLP base port's value is invalid. """ mode_config: PackageModeConfig = request.param + mode_name = mode_config.mode_name clp_config_obj = mode_config.clp_config + logger.info("Setting up the '%s' package...", mode_name) + # Assign ports based on the clp base port CLI option. + logger.debug("Assigning ports to the components in the '%s' package...", mode_name) base_port_string = request.config.getoption("--base-port") try: base_port = int(base_port_string) except ValueError as err: err_msg = f"Invalid value '{base_port_string}' for '--base-port'; expected an integer." + logger.error(construct_log_err_msg(err_msg)) raise ValueError(err_msg) from err assign_ports_from_base(base_port, clp_config_obj) @@ -43,4 +52,5 @@ def fixt_package_test_config( try: yield package_test_config finally: + logger.info("Cleaning up the '%s' package...", mode_name) package_test_config.temp_config_file_path.unlink(missing_ok=True) diff --git a/integration-tests/tests/package_tests/clp_json/test_clp_json.py b/integration-tests/tests/package_tests/clp_json/test_clp_json.py index d3c48af72f..a3c779e7ac 100644 --- a/integration-tests/tests/package_tests/clp_json/test_clp_json.py +++ b/integration-tests/tests/package_tests/clp_json/test_clp_json.py @@ -1,7 +1,5 @@ """Tests for the clp-json package.""" -import logging - import pytest from clp_py_utils.clp_config import ( ClpConfig, @@ -16,9 +14,6 @@ from tests.utils.clp_mode_utils import CLP_API_SERVER_COMPONENT, CLP_BASE_COMPONENTS from tests.utils.config import PackageInstance, PackageModeConfig -logger = logging.getLogger(__name__) - - # Mode description for this module. CLP_JSON_MODE = PackageModeConfig( mode_name="clp-json", @@ -36,27 +31,26 @@ pytestmark = [ pytest.mark.package, pytest.mark.clp_json, - pytest.mark.parametrize("fixt_package_test_config", [CLP_JSON_MODE], indirect=True), + pytest.mark.parametrize( + "fixt_package_test_config", [CLP_JSON_MODE], indirect=True, ids=[CLP_JSON_MODE.mode_name] + ), ] @pytest.mark.startup def test_clp_json_startup(fixt_package_instance: PackageInstance) -> None: """ - Validate that the `clp-json` package starts up successfully. + Validates that the `clp-json` package starts up successfully. :param fixt_package_instance: """ validate_package_instance(fixt_package_instance) - log_msg = "test_clp_json_startup was successful." - logger.info(log_msg) - @pytest.mark.compression def test_clp_json_compression(fixt_package_instance: PackageInstance) -> None: """ - Validate that the `clp-json` package successfully compresses some dataset. + Validates that the `clp-json` package successfully compresses some dataset. :param fixt_package_instance: """ @@ -65,14 +59,11 @@ def test_clp_json_compression(fixt_package_instance: PackageInstance) -> None: # TODO: compress some dataset and check the correctness of compression. assert True - log_msg = "test_clp_json_compression was successful." - logger.info(log_msg) - @pytest.mark.search def test_clp_json_search(fixt_package_instance: PackageInstance) -> None: """ - Validate that the `clp-json` package successfully searches some dataset. + Validates that the `clp-json` package successfully searches some dataset. :param fixt_package_instance: """ @@ -83,6 +74,3 @@ def test_clp_json_search(fixt_package_instance: PackageInstance) -> None: # TODO: search through that dataset and check the correctness of the search results. assert True - - log_msg = "test_clp_json_search was successful." - logger.info(log_msg) diff --git a/integration-tests/tests/package_tests/clp_text/test_clp_text.py b/integration-tests/tests/package_tests/clp_text/test_clp_text.py index dbbc862f4c..076b8d9f5a 100644 --- a/integration-tests/tests/package_tests/clp_text/test_clp_text.py +++ b/integration-tests/tests/package_tests/clp_text/test_clp_text.py @@ -1,7 +1,5 @@ """Tests for the clp-text package.""" -import logging - import pytest from clp_py_utils.clp_config import ( ClpConfig, @@ -16,9 +14,6 @@ from tests.utils.clp_mode_utils import CLP_BASE_COMPONENTS from tests.utils.config import PackageInstance, PackageModeConfig -logger = logging.getLogger(__name__) - - # Mode description for this module. CLP_TEXT_MODE = PackageModeConfig( mode_name="clp-text", @@ -38,14 +33,17 @@ pytestmark = [ pytest.mark.package, pytest.mark.clp_text, - pytest.mark.parametrize("fixt_package_test_config", [CLP_TEXT_MODE], indirect=True), + pytest.mark.parametrize( + "fixt_package_test_config", [CLP_TEXT_MODE], indirect=True, ids=[CLP_TEXT_MODE.mode_name] + ), ] @pytest.mark.startup def test_clp_text_startup(fixt_package_instance: PackageInstance) -> None: - """Tests package startup.""" - validate_package_instance(fixt_package_instance) + """ + Validates that the `clp-text` package starts up successfully. - log_msg = "test_clp_text_startup was successful." - logger.info(log_msg) + :param fixt_package_instance: + """ + validate_package_instance(fixt_package_instance) diff --git a/integration-tests/tests/test_identity_transformation.py b/integration-tests/tests/test_identity_transformation.py index 9d95f357a8..b2e1c4501a 100644 --- a/integration-tests/tests/test_identity_transformation.py +++ b/integration-tests/tests/test_identity_transformation.py @@ -5,7 +5,7 @@ import pytest -from tests.utils.asserting_utils import run_and_assert +from tests.utils.asserting_utils import run_and_log_to_file from tests.utils.config import ( ClpCorePathConfig, CompressionTestPathConfig, @@ -73,10 +73,10 @@ def test_clp_identity_transform( src_path, ] # fmt: on - run_and_assert(compression_cmd) + run_and_log_to_file(request, compression_cmd) decompression_cmd = [bin_path, "x", compression_path, decompression_path] - run_and_assert(decompression_cmd) + run_and_log_to_file(request, decompression_cmd) input_path = test_paths.logs_source_dir output_path = test_paths.decompression_dir @@ -113,7 +113,7 @@ def test_clp_s_identity_transform( logs_source_dir=integration_test_logs.extraction_dir, integration_test_path_config=integration_test_path_config, ) - _clp_s_compress_and_decompress(clp_core_path_config, test_paths) + _clp_s_compress_and_decompress(request, clp_core_path_config, test_paths) # Recompress the decompressed output that's consolidated into a single json file, and decompress # it again to verify consistency. The compression input of the second iteration points to the @@ -126,7 +126,7 @@ def test_clp_s_identity_transform( logs_source_dir=test_paths.decompression_dir, integration_test_path_config=integration_test_path_config, ) - _clp_s_compress_and_decompress(clp_core_path_config, consolidated_json_test_paths) + _clp_s_compress_and_decompress(request, clp_core_path_config, consolidated_json_test_paths) _consolidated_json_file_name = "original" input_path = consolidated_json_test_paths.logs_source_dir / _consolidated_json_file_name @@ -140,6 +140,7 @@ def test_clp_s_identity_transform( def _clp_s_compress_and_decompress( + request: pytest.FixtureRequest, clp_core_path_config: ClpCorePathConfig, test_paths: CompressionTestPathConfig, ) -> None: @@ -148,5 +149,6 @@ def _clp_s_compress_and_decompress( src_path = str(test_paths.logs_source_dir) compression_path = str(test_paths.compression_dir) decompression_path = str(test_paths.decompression_dir) - run_and_assert([bin_path, "c", compression_path, src_path]) - run_and_assert([bin_path, "x", compression_path, decompression_path]) + + run_and_log_to_file(request, [bin_path, "c", compression_path, src_path]) + run_and_log_to_file(request, [bin_path, "x", compression_path, decompression_path]) diff --git a/integration-tests/tests/utils/asserting_utils.py b/integration-tests/tests/utils/asserting_utils.py index 0a9b78e424..cbdd399fc1 100644 --- a/integration-tests/tests/utils/asserting_utils.py +++ b/integration-tests/tests/utils/asserting_utils.py @@ -1,8 +1,8 @@ """Utilities that raise pytest assertions on failure.""" import logging -import shlex import subprocess +from pathlib import Path from typing import Any import pytest @@ -12,29 +12,26 @@ from tests.utils.clp_mode_utils import compare_mode_signatures from tests.utils.config import PackageInstance from tests.utils.docker_utils import list_running_services_in_compose_project +from tests.utils.logging_utils import construct_log_err_msg from tests.utils.utils import load_yaml_to_dict logger = logging.getLogger(__name__) -def run_and_assert(cmd: list[str], **kwargs: Any) -> subprocess.CompletedProcess[Any]: +def run_and_log_to_file(request: pytest.FixtureRequest, cmd: list[str], **kwargs: Any) -> None: """ - Runs a command with subprocess and asserts that it succeeds with pytest. + Runs a command with subprocess. + :param request: Pytest fixture request. :param cmd: Command and arguments to execute. :param kwargs: Additional keyword arguments passed through to the subprocess. - :return: The completed process object, for inspection or further handling. - :raise: pytest.fail if the command exits with a non-zero return code. + :raise: Propagates `subprocess.run`'s errors. """ - logger.info("Running command: %s", shlex.join(cmd)) - - try: - proc = subprocess.run(cmd, check=True, **kwargs) - except subprocess.CalledProcessError as e: - pytest.fail(f"Command failed: {' '.join(cmd)}: {e}") - except subprocess.TimeoutExpired as e: - pytest.fail(f"Command timed out: {' '.join(cmd)}: {e}") - return proc + log_file_path = Path(request.config.getini("log_file_path")) + with log_file_path.open("ab") as log_file: + log_debug_msg = f"Now running command: {cmd}" + logger.debug(log_debug_msg) + subprocess.run(cmd, stdout=log_file, stderr=log_file, check=True, **kwargs) def validate_package_instance(package_instance: PackageInstance) -> None: @@ -45,6 +42,9 @@ def validate_package_instance(package_instance: PackageInstance) -> None: :param package_instance: """ + mode_name = package_instance.package_test_config.mode_config.mode_name + logger.info("Validating that the '%s' package is running correctly...", mode_name) + # Ensure that all package components are running. _validate_package_running(package_instance) @@ -60,6 +60,9 @@ def _validate_package_running(package_instance: PackageInstance) -> None: :param package_instance: :raise pytest.fail: if the sets of running services and required components do not match. """ + mode_name = package_instance.package_test_config.mode_config.mode_name + logger.debug("Validating that all components of the '%s' package are running...", mode_name) + # Get list of services currently running in the Compose project. instance_id = package_instance.clp_instance_id project_name = f"clp-package-{instance_id}" @@ -70,17 +73,19 @@ def _validate_package_running(package_instance: PackageInstance) -> None: if required_components == running_services: return - fail_msg = "Component mismatch." + # Construct error message. + err_msg = f"Component validation failed for the {mode_name} package test." missing_components = required_components - running_services if missing_components: - fail_msg += f"\nMissing components: {missing_components}." + err_msg += f" Missing components: {missing_components}." unexpected_components = running_services - required_components if unexpected_components: - fail_msg += f"\nUnexpected services: {unexpected_components}." + err_msg += f" Unexpected services: {unexpected_components}." - pytest.fail(fail_msg) + logger.error(construct_log_err_msg(err_msg)) + pytest.fail(err_msg) def _validate_running_mode_correct(package_instance: PackageInstance) -> None: @@ -94,13 +99,22 @@ def _validate_running_mode_correct(package_instance: PackageInstance) -> None: :raise pytest.fail: if the ClpConfig object cannot be validated. :raise pytest.fail: if the running ClpConfig does not match the intended ClpConfig. """ + mode_name = package_instance.package_test_config.mode_config.mode_name + logger.debug( + "Validating that the '%s' package is running in the correct configuration...", mode_name + ) + shared_config_dict = load_yaml_to_dict(package_instance.shared_config_file_path) try: running_config = ClpConfig.model_validate(shared_config_dict) except ValidationError as err: - pytest.fail(f"Shared config failed validation: {err}") + err_msg = f"The shared config file could not be validated: {err}" + logger.error(construct_log_err_msg(err_msg)) + pytest.fail(err_msg) intended_config = package_instance.package_test_config.mode_config.clp_config if not compare_mode_signatures(intended_config, running_config): - pytest.fail("Mode mismatch: running configuration does not match intended configuration.") + err_msg = f"Mode validation failed for the {mode_name} package test." + logger.error(construct_log_err_msg(err_msg)) + pytest.fail(err_msg) diff --git a/integration-tests/tests/utils/config.py b/integration-tests/tests/utils/config.py index 76a8e2d43a..57cc1734a6 100644 --- a/integration-tests/tests/utils/config.py +++ b/integration-tests/tests/utils/config.py @@ -2,6 +2,7 @@ from __future__ import annotations +import logging import re from dataclasses import dataclass, field, InitVar from pathlib import Path @@ -19,6 +20,8 @@ validate_file_exists, ) +logger = logging.getLogger(__name__) + @dataclass(frozen=True) class ClpCorePathConfig: @@ -157,7 +160,8 @@ def temp_config_file_path(self) -> Path: return self.path_config.temp_config_dir / f"clp-config-{self.mode_config.mode_name}.yaml" def _write_temp_config_file(self) -> None: - """Writes the temporary config file for this package test.""" + """Writes the temporary config file for this package.""" + logger.debug("Writing the config file for the '%s' package...", self.mode_config.mode_name) temp_config_file_path = self.temp_config_file_path payload = self.mode_config.clp_config.dump_to_primitive_dict() # type: ignore[no-untyped-call] diff --git a/integration-tests/tests/utils/logging_utils.py b/integration-tests/tests/utils/logging_utils.py new file mode 100644 index 0000000000..46570e14ce --- /dev/null +++ b/integration-tests/tests/utils/logging_utils.py @@ -0,0 +1,19 @@ +"""Utilities for logging during the test run.""" + +BLUE = "\033[34m" +BOLD = "\033[1m" +RESET = "\033[0m" + + +def construct_log_err_msg(err_msg: str) -> str: + """ + Append a signal that directs readers to the test output log file. + + :param err_msg: The base error message onto which the signal will be appended. + :return: An error message that directs readers to look in the test output log file. + + """ + return ( + err_msg + + " Check the full test output log for more information (see test header for file path)." + ) diff --git a/integration-tests/tests/utils/package_utils.py b/integration-tests/tests/utils/package_utils.py index 31254b8791..18629463ce 100644 --- a/integration-tests/tests/utils/package_utils.py +++ b/integration-tests/tests/utils/package_utils.py @@ -1,17 +1,29 @@ """Provides utility functions related to the CLP package used across `integration-tests`.""" -from tests.utils.asserting_utils import run_and_assert +import logging +from subprocess import SubprocessError + +import pytest + +from tests.utils.asserting_utils import run_and_log_to_file from tests.utils.config import PackageTestConfig +from tests.utils.logging_utils import construct_log_err_msg + +logger = logging.getLogger(__name__) + DEFAULT_CMD_TIMEOUT_SECONDS = 120.0 -def start_clp_package(package_test_config: PackageTestConfig) -> None: +def start_clp_package( + request: pytest.FixtureRequest, package_test_config: PackageTestConfig +) -> None: """ Starts an instance of the CLP package. + :param request: Pytest fixture request. :param package_test_config: - :raise: Propagates `run_and_assert`'s errors. + :raise: Propagates `run_and_log_to_file`'s errors. """ path_config = package_test_config.path_config start_script_path = path_config.start_script_path @@ -23,15 +35,25 @@ def start_clp_package(package_test_config: PackageTestConfig) -> None: "--config", str(temp_config_file_path), ] # fmt: on - run_and_assert(start_cmd, timeout=DEFAULT_CMD_TIMEOUT_SECONDS) + + try: + run_and_log_to_file(request, start_cmd, timeout=DEFAULT_CMD_TIMEOUT_SECONDS) + except (SubprocessError, OSError): + mode_name = package_test_config.mode_config.mode_name + err_msg = f"The '{mode_name}' package failed to start." + logger.error(construct_log_err_msg(err_msg)) + pytest.fail(err_msg) -def stop_clp_package(package_test_config: PackageTestConfig) -> None: +def stop_clp_package( + request: pytest.FixtureRequest, package_test_config: PackageTestConfig +) -> None: """ Stops the running instance of the CLP package. + :param request: Pytest fixture request. :param package_test_config: - :raise: Propagates `run_and_assert`'s errors. + :raise: Propagates `run_and_log_to_file`'s errors. """ path_config = package_test_config.path_config stop_script_path = path_config.stop_script_path @@ -43,4 +65,11 @@ def stop_clp_package(package_test_config: PackageTestConfig) -> None: "--config", str(temp_config_file_path), ] # fmt: on - run_and_assert(stop_cmd, timeout=DEFAULT_CMD_TIMEOUT_SECONDS) + + try: + run_and_log_to_file(request, stop_cmd, timeout=DEFAULT_CMD_TIMEOUT_SECONDS) + except (SubprocessError, OSError): + mode_name = package_test_config.mode_config.mode_name + err_msg = f"The '{mode_name}' package failed to stop." + logger.error(construct_log_err_msg(err_msg)) + pytest.fail(err_msg) diff --git a/integration-tests/tests/utils/port_utils.py b/integration-tests/tests/utils/port_utils.py index cfb1b47c45..c9086fc89c 100644 --- a/integration-tests/tests/utils/port_utils.py +++ b/integration-tests/tests/utils/port_utils.py @@ -1,5 +1,6 @@ """Functions for facilitating the port connections for the CLP package.""" +import logging import socket from dataclasses import dataclass from typing import Any @@ -9,6 +10,10 @@ REDUCER_COMPONENT_NAME, ) +from tests.utils.logging_utils import construct_log_err_msg + +logger = logging.getLogger(__name__) + # Port constants. MIN_NON_PRIVILEGED_PORT = 1024 MAX_PORT = 65535 @@ -76,6 +81,7 @@ def _check_ports_available(host: str, port_range: range) -> None: f"Port '{port}' in the desired range ({range_str}) is already in use. " "Choose a different port range for the test environment." ) + logger.error(construct_log_err_msg(err_msg)) raise ValueError(err_msg) @@ -160,7 +166,8 @@ def _validate_port_range_bounds(port_range: range) -> None: required_range_str = _format_port_range(port_range) valid_range_str = _format_port_range(VALID_PORT_RANGE) err_msg = ( - f"The port range derived from --base-port ({required_range_str}) must fall within" + f"The port range derived from '--base-port' ({required_range_str}) must fall within" f" the range of valid ports ({valid_range_str})." ) + logger.error(construct_log_err_msg(err_msg)) raise ValueError(err_msg)