diff --git a/conftest.py b/conftest.py index 0e5c6584c0..9f9a946393 100644 --- a/conftest.py +++ b/conftest.py @@ -27,7 +27,9 @@ from pytest_testconfig import config as py_config import utilities.cluster -import utilities.infra + +# TODO: Remove this import when utilities modules are refactored... +import utilities.infra # noqa from libs.storage.config import StorageClassConfig from utilities.bitwarden import get_cnv_tests_secret_by_name from utilities.constants import ( @@ -52,6 +54,7 @@ config_default_storage_class, deploy_run_in_progress_config_map, deploy_run_in_progress_namespace, + generate_os_matrix_dicts, get_artifactory_server_url, get_base_matrix_name, get_cnv_version_explorer_url, @@ -62,6 +65,7 @@ separator, skip_if_pytest_flags_exists, stop_if_run_in_progress, + update_latest_os_config, ) LOGGER = logging.getLogger(__name__) @@ -755,34 +759,6 @@ def pytest_generate_tests(metafunc): def pytest_sessionstart(session): - # TODO: Reduce cognitive complexity - def _update_os_related_config(): - # Save the default windows_os_matrix before it is updated - # with runtime windows_os_matrix value(s). - # Some tests extract a single OS from the matrix and may fail if running with - # passed values from cli - if windows_os_matrix := py_config.get("windows_os_matrix"): - py_config["system_windows_os_matrix"] = windows_os_matrix - - if rhel_os_matrix := py_config.get("rhel_os_matrix"): - py_config["system_rhel_os_matrix"] = rhel_os_matrix - - # Update OS matrix list with the latest OS if running with os_group - if session.config.getoption("latest_rhel") and rhel_os_matrix: - py_config["rhel_os_matrix"] = [utilities.infra.generate_latest_os_dict(os_list=rhel_os_matrix)] - py_config["instance_type_rhel_os_matrix"] = [ - utilities.infra.generate_latest_os_dict(os_list=py_config["instance_type_rhel_os_matrix"]) - ] - - if session.config.getoption("latest_windows") and windows_os_matrix: - py_config["windows_os_matrix"] = [utilities.infra.generate_latest_os_dict(os_list=windows_os_matrix)] - - if session.config.getoption("latest_centos") and (centos_os_matrix := py_config.get("centos_os_matrix")): - py_config["centos_os_matrix"] = [utilities.infra.generate_latest_os_dict(os_list=centos_os_matrix)] - - if session.config.getoption("latest_fedora") and (fedora_os_matrix := py_config.get("fedora_os_matrix")): - py_config["fedora_os_matrix"] = [utilities.infra.generate_latest_os_dict(os_list=fedora_os_matrix)] - data_collector_dict = set_data_collector_values(base_dir=session.config.getoption("data_collector_output_dir")) shutil.rmtree( data_collector_dict["data_collector_base_directory"], @@ -802,7 +778,8 @@ def _update_os_related_config(): # with runtime storage_class_matrix value(s) py_config["system_storage_class_matrix"] = py_config.get("storage_class_matrix", []) - _update_os_related_config() + generate_os_matrix_dicts(os_dict=py_config) + update_latest_os_config(session_config=session.config) matrix_addoptions = [matrix for matrix in session.config.invocation_params.args if "-matrix=" in matrix] for matrix_addoption in matrix_addoptions: diff --git a/tests/global_config_arm64.py b/tests/global_config_arm64.py index ed3645a469..f77fde2ea3 100644 --- a/tests/global_config_arm64.py +++ b/tests/global_config_arm64.py @@ -12,11 +12,6 @@ Images, StorageClassNames, ) -from utilities.infra import get_latest_os_dict_list -from utilities.os_utils import ( - generate_linux_instance_type_os_matrix, - generate_os_matrix_dict, -) from utilities.storage import HppCsiStorageClass global config @@ -51,21 +46,12 @@ storage_class_a = StorageClassNames.IO2_CSI storage_class_b = StorageClassNames.IO2_CSI -rhel_os_matrix = generate_os_matrix_dict(os_name="rhel", supported_operating_systems=["rhel-9-5", "rhel-9-6"]) -fedora_os_matrix = generate_os_matrix_dict(os_name="fedora", supported_operating_systems=["fedora-42"]) -centos_os_matrix = generate_os_matrix_dict(os_name="centos", supported_operating_systems=["centos-stream-9"]) - -latest_rhel_os_dict, latest_fedora_os_dict, latest_centos_os_dict = get_latest_os_dict_list( - os_list=[rhel_os_matrix, fedora_os_matrix, centos_os_matrix] -) +rhel_os_list = ["rhel-9-5", "rhel-9-6"] +fedora_os_list = ["fedora-42"] +centos_os_list = ["centos-stream-9"] -# Modify instance_type_rhel_os_matrix for arm64 -instance_type_rhel_os_matrix = generate_linux_instance_type_os_matrix( - os_name="rhel", preferences=[RHEL10_PREFERENCE], arch_suffix=ARM_64 -) -instance_type_fedora_os_matrix = generate_linux_instance_type_os_matrix( - os_name=OS_FLAVOR_FEDORA, preferences=[OS_FLAVOR_FEDORA], arch_suffix=ARM_64 -) +instance_type_rhel_os_list = [RHEL10_PREFERENCE] +instance_type_fedora_os_list = [OS_FLAVOR_FEDORA] for _dir in dir(): if not config: # noqa: F821 diff --git a/tests/global_config_interop.py b/tests/global_config_interop.py index f08ad2b7a6..d90584e776 100644 --- a/tests/global_config_interop.py +++ b/tests/global_config_interop.py @@ -2,14 +2,9 @@ import pytest_testconfig -from utilities.infra import generate_latest_os_dict - global config global_config = pytest_testconfig.load_python(py_file="tests/global_config.py", encoding="utf-8") -fedora_os_matrix = [ - generate_latest_os_dict(os_list=config["fedora_os_matrix"]) # noqa: F821 -] for _dir in dir(): if not config: # noqa: F821 diff --git a/tests/global_config_s390x.py b/tests/global_config_s390x.py index bca2087c8b..883d27f9da 100644 --- a/tests/global_config_s390x.py +++ b/tests/global_config_s390x.py @@ -1,36 +1,28 @@ from typing import Any -import utilities.constants from utilities.constants import ( + CENTOS_STREAM9_PREFERENCE, EXPECTED_CLUSTER_INSTANCE_TYPE_LABELS, + OS_FLAVOR_FEDORA, PREFERENCE_STR, + RHEL9_PREFERENCE, S390X, ) -from utilities.infra import get_latest_os_dict_list -from utilities.os_utils import generate_linux_instance_type_os_matrix, generate_os_matrix_dict global config EXPECTED_CLUSTER_INSTANCE_TYPE_LABELS[PREFERENCE_STR] = f"rhel.9.{S390X}" -rhel_os_matrix = generate_os_matrix_dict( - os_name="rhel", - supported_operating_systems=[ - "rhel-8-10", - "rhel-9-6", - ], -) -fedora_os_matrix = generate_os_matrix_dict(os_name="fedora", supported_operating_systems=["fedora-42"]) -centos_os_matrix = generate_os_matrix_dict(os_name="centos", supported_operating_systems=["centos-stream-9"]) -instance_type_rhel_os_matrix = generate_linux_instance_type_os_matrix( - os_name="rhel", preferences=[utilities.constants.RHEL9_PREFERENCE], arch_suffix=S390X -) +rhel_os_list = ["rhel-8-10", "rhel-9-6"] +fedora_os_list = ["fedora-42"] +centos_os_list = ["centos-stream-9"] + +instance_type_rhel_os_list = [RHEL9_PREFERENCE] +instance_type_fedora_os_list = [OS_FLAVOR_FEDORA] +instance_type_centos_os_list = [CENTOS_STREAM9_PREFERENCE] -latest_rhel_os_dict, latest_fedora_os_dict, latest_centos_os_dict = get_latest_os_dict_list( - os_list=[rhel_os_matrix, fedora_os_matrix, centos_os_matrix] -) for _dir in dir(): if not config: # noqa: F821 diff --git a/tests/global_config_x86_64.py b/tests/global_config_x86_64.py index c8c42632a9..0887976065 100644 --- a/tests/global_config_x86_64.py +++ b/tests/global_config_x86_64.py @@ -8,52 +8,17 @@ RHEL9_PREFERENCE, RHEL10_PREFERENCE, ) -from utilities.infra import get_latest_os_dict_list -from utilities.os_utils import ( - generate_linux_instance_type_os_matrix, - generate_os_matrix_dict, -) global config -rhel_os_matrix = generate_os_matrix_dict( - os_name="rhel", - supported_operating_systems=[ - "rhel-8-10", - "rhel-9-6", - ], -) - -windows_os_matrix = generate_os_matrix_dict( - os_name="windows", - supported_operating_systems=[ - "win-10", - "win-2016", - "win-2019", - "win-11", - "win-2022", - "win-2025", - ], -) - -fedora_os_matrix = generate_os_matrix_dict(os_name="fedora", supported_operating_systems=["fedora-43"]) -centos_os_matrix = generate_os_matrix_dict(os_name="centos", supported_operating_systems=["centos-stream-9"]) -instance_type_rhel_os_matrix = generate_linux_instance_type_os_matrix( - os_name="rhel", preferences=[RHEL8_PREFERENCE, RHEL9_PREFERENCE, RHEL10_PREFERENCE] -) -instance_type_centos_os_matrix = generate_linux_instance_type_os_matrix( - os_name="centos.stream", preferences=[CENTOS_STREAM9_PREFERENCE, CENTOS_STREAM10_PREFERENCE] -) -instance_type_fedora_os_matrix = generate_linux_instance_type_os_matrix( - os_name=OS_FLAVOR_FEDORA, preferences=[OS_FLAVOR_FEDORA] -) +rhel_os_list = ["rhel-8-10", "rhel-9-6"] +windows_os_list = ["win-10", "win-2016", "win-2019", "win-11", "win-2022", "win-2025"] +fedora_os_list = ["fedora-43"] +centos_os_list = ["centos-stream-9"] -( - latest_rhel_os_dict, - latest_windows_os_dict, - latest_fedora_os_dict, - latest_centos_os_dict, -) = get_latest_os_dict_list(os_list=[rhel_os_matrix, windows_os_matrix, fedora_os_matrix, centos_os_matrix]) +instance_type_rhel_os_list = [RHEL8_PREFERENCE, RHEL9_PREFERENCE, RHEL10_PREFERENCE] +instance_type_centos_os_list = [CENTOS_STREAM9_PREFERENCE, CENTOS_STREAM10_PREFERENCE] +instance_type_fedora_os_list = [OS_FLAVOR_FEDORA] for _dir in dir(): diff --git a/utilities/infra.py b/utilities/infra.py index 63ca533edf..2f82992a85 100644 --- a/utilities/infra.py +++ b/utilities/infra.py @@ -70,7 +70,6 @@ NamespacesNames, ) from utilities.exceptions import ( - OsDictNotFoundError, UrlNotFoundError, UtilityPodNotFoundError, ) @@ -165,44 +164,6 @@ def generate_namespace_name(file_path): return (file_path.strip(".py").replace("/", "-").replace("_", "-"))[-63:].split("-", 1)[-1] -def generate_latest_os_dict(os_list): - """ - Get latest os dict. - - Args: - os_list (list): []_os_matrix - a list of dicts. - - Returns: - dict: {Latest OS name: latest supported OS dict} else raises an exception. - - Raises: - OsDictNotFoundError: If no os matched. - """ - for os_dict in os_list: - for os_version, os_values in os_dict.items(): - if os_values.get("latest_released"): - return {os_version: os_values} - - raise OsDictNotFoundError(f"No OS is marked as 'latest_released': {os_list}") - - -def get_latest_os_dict_list(os_list): - """ - Get latest os dict generated by 'generate_latest_os_dict()' - This will extract the dict from `generate_latest_os_dict()` without the name key. - - Args: - os_list (list): [rhel|windows|fedora]_os_matrix - a list of dicts - - Returns: - list: List of oses dict [{latest supported OS dict}] - """ - res = [] - for _os in os_list: - res.append(list(generate_latest_os_dict(os_list=_os).values())[0]) - return res - - def get_pods(client: DynamicClient, namespace: Namespace, label: str = "") -> list[Pod]: return list( Pod.get( diff --git a/utilities/os_utils.py b/utilities/os_utils.py index 8395403a87..c843b68ad4 100644 --- a/utilities/os_utils.py +++ b/utilities/os_utils.py @@ -27,6 +27,7 @@ WORKLOAD_STR, Images, ) +from utilities.exceptions import OsDictNotFoundError LOGGER = logging.getLogger(__name__) @@ -294,3 +295,24 @@ def _format_data_source_name(preference_name: str) -> str: instance_types.append({arch_preference: preference_config}) return instance_types + + +def generate_latest_os_dict(os_matrix: list[dict[str, Any]]) -> dict[str, Any]: + """ + Get latest os dict. + + Args: + os_matrix (list): []_os_matrix - a list of dicts. + + Returns: + dict: Latest supported OS dict (the os_values payload) or raises an exception. + + Raises: + OsDictNotFoundError: If no os matched. + """ + for matrix in os_matrix: + for os_values in matrix.values(): + if os_values.get(LATEST_RELEASE_STR): + return os_values + + raise OsDictNotFoundError(f"No OS is marked as '{LATEST_RELEASE_STR}': {os_matrix}") diff --git a/utilities/pytest_utils.py b/utilities/pytest_utils.py index 5d67a9b104..0bf24ed3cb 100644 --- a/utilities/pytest_utils.py +++ b/utilities/pytest_utils.py @@ -15,15 +15,19 @@ from ocp_resources.resource import ResourceEditor from pytest_testconfig import config as py_config +from utilities.architecture import get_cluster_architecture from utilities.bitwarden import get_cnv_tests_secret_by_name from utilities.constants import ( + AMD_64, CNV_TEST_RUN_IN_PROGRESS, CNV_TEST_RUN_IN_PROGRESS_NS, CNV_TESTS_CONTAINER, POD_SECURITY_NAMESPACE_LABELS, + S390X, SANITY_TESTS_FAILURE, TIMEOUT_2MIN, TIMEOUT_5MIN, + X86_64, ) from utilities.data_collector import ( collect_default_cnv_must_gather_with_vm_gather, @@ -31,6 +35,11 @@ write_to_file, ) from utilities.exceptions import MissingEnvironmentVariableError +from utilities.os_utils import ( + generate_latest_os_dict, + generate_linux_instance_type_os_matrix, + generate_os_matrix_dict, +) LOGGER = logging.getLogger(__name__) @@ -332,3 +341,108 @@ def exit_pytest_execution( if junitxml_property: junitxml_property(name="exit_code", value=return_code) pytest.exit(reason=log_message, returncode=return_code) + + +def generate_os_matrix_dicts(os_dict: dict[str, list[str]]) -> None: + """ + Generate and populate OS matrix and related dictionaries in py_config. + + Args: + os_dict (dict[str, list[str]]): A dictionary containing lists of supported OS names for each + relevant OS family. Keys can include: + - "rhel_os_list" + - "fedora_os_list" + - "centos_os_list" + - "windows_os_list" + - "instance_type_rhel_os_list" + - "instance_type_fedora_os_list" + - "instance_type_centos_os_list" + """ + + if rhel_os_list := os_dict.get("rhel_os_list"): + py_config["rhel_os_matrix"] = generate_os_matrix_dict(os_name="rhel", supported_operating_systems=rhel_os_list) + py_config["latest_rhel_os_dict"] = generate_latest_os_dict(os_matrix=py_config["rhel_os_matrix"]) + if fedora_os_list := os_dict.get("fedora_os_list"): + py_config["fedora_os_matrix"] = generate_os_matrix_dict( + os_name="fedora", supported_operating_systems=fedora_os_list + ) + py_config["latest_fedora_os_dict"] = generate_latest_os_dict(os_matrix=py_config["fedora_os_matrix"]) + if centos_os_list := os_dict.get("centos_os_list"): + py_config["centos_os_matrix"] = generate_os_matrix_dict( + os_name="centos", supported_operating_systems=centos_os_list + ) + py_config["latest_centos_os_dict"] = generate_latest_os_dict(os_matrix=py_config["centos_os_matrix"]) + if windows_os_list := os_dict.get("windows_os_list"): + py_config["windows_os_matrix"] = generate_os_matrix_dict( + os_name="windows", supported_operating_systems=windows_os_list + ) + py_config["latest_windows_os_dict"] = generate_latest_os_dict(os_matrix=py_config["windows_os_matrix"]) + + arch = get_cluster_architecture() + cpu_arch = None if arch in (AMD_64, X86_64) else arch + if instance_type_rhel_os_list := os_dict.get("instance_type_rhel_os_list"): + py_config["instance_type_rhel_os_matrix"] = generate_linux_instance_type_os_matrix( + os_name="rhel", preferences=instance_type_rhel_os_list, arch_suffix=cpu_arch + ) + py_config["latest_instance_type_rhel_os_dict"] = generate_latest_os_dict( + os_matrix=py_config["instance_type_rhel_os_matrix"] + ) + if instance_type_fedora_os_list := os_dict.get("instance_type_fedora_os_list"): + py_config["instance_type_fedora_os_matrix"] = generate_linux_instance_type_os_matrix( + os_name="fedora", preferences=instance_type_fedora_os_list, arch_suffix=cpu_arch + ) + if instance_type_centos_os_list := os_dict.get("instance_type_centos_os_list"): + py_config["instance_type_centos_os_matrix"] = generate_linux_instance_type_os_matrix( + os_name="centos", preferences=instance_type_centos_os_list, arch_suffix=cpu_arch if arch != S390X else None + ) + + +def update_latest_os_config(session_config: pytest.Config) -> None: + """ + Update py_config with OS-related configuration based on session configuration. + + Args: + session_config (pytest.Config): The pytest session configuration object. + + Side effects: + Mutates the module-level py_config dict. Preserves original matrices in + system_windows_os_matrix and system_rhel_os_matrix. Updates rhel_os_matrix + and instance_type_rhel_os_matrix when --latest_rhel is set, windows_os_matrix + when --latest_windows is set, centos_os_matrix when --latest_centos is set, + and fedora_os_matrix when --latest_fedora is set. + """ + + # Save the default windows_os_matrix before it is updated + # with runtime windows_os_matrix value(s). + # Some tests extract a single OS from the matrix and may fail if running with + # passed values from cli + if windows_os_matrix := py_config.get("windows_os_matrix"): + py_config["system_windows_os_matrix"] = windows_os_matrix + + if rhel_os_matrix := py_config.get("rhel_os_matrix"): + py_config["system_rhel_os_matrix"] = rhel_os_matrix + + # Update OS matrix list with the latest OS if running with os_group + if session_config.getoption("latest_rhel") and rhel_os_matrix: + latest_rhel_os_dict = py_config.get("latest_rhel_os_dict", {}) + py_config["rhel_os_matrix"] = [{f"rhel.{latest_rhel_os_dict.get('os_version', 'latest')}": latest_rhel_os_dict}] + latest_instance_type_rhel_os_dict = py_config.get("latest_instance_type_rhel_os_dict", {}) + py_config["instance_type_rhel_os_matrix"] = [ + {latest_instance_type_rhel_os_dict.get("preference", "rhel.latest"): latest_instance_type_rhel_os_dict} + ] + + if session_config.getoption("latest_windows") and windows_os_matrix: + latest_windows_os_dict = py_config.get("latest_windows_os_dict", {}) + py_config["windows_os_matrix"] = [ + {f"windows.{latest_windows_os_dict.get('os_version', 'latest')}": latest_windows_os_dict} + ] + + if session_config.getoption("latest_centos") and py_config.get("centos_os_matrix"): + latest_centos_os_dict = py_config.get("latest_centos_os_dict", {}) + py_config["centos_os_matrix"] = [ + {f"centos-stream.{latest_centos_os_dict.get('os_version', 'latest')}": latest_centos_os_dict} + ] + + if session_config.getoption("latest_fedora") and py_config.get("fedora_os_matrix"): + latest_fedora_os_dict = py_config.get("latest_fedora_os_dict", {}) + py_config["fedora_os_matrix"] = [{"fedora": latest_fedora_os_dict}] diff --git a/utilities/unittests/test_os_utils.py b/utilities/unittests/test_os_utils.py index e1a9d14b5f..54bfbbb384 100644 --- a/utilities/unittests/test_os_utils.py +++ b/utilities/unittests/test_os_utils.py @@ -6,11 +6,13 @@ import pytest +from utilities.exceptions import OsDictNotFoundError from utilities.os_utils import ( CENTOS_OS_MAPPING, FEDORA_OS_MAPPING, RHEL_OS_MAPPING, WINDOWS_OS_MAPPING, + generate_latest_os_dict, generate_linux_instance_type_os_matrix, generate_os_matrix_dict, ) @@ -314,3 +316,120 @@ def test_centos_os_mapping_structure(self): assert "workload" in CENTOS_OS_MAPPING assert "flavor" in CENTOS_OS_MAPPING assert "centos-stream-9" in CENTOS_OS_MAPPING + + +class TestGenerateLatestOsDict: + """Test cases for generate_latest_os_dict function""" + + def test_returns_latest_os_dict_when_found(self): + """Test returning the OS dict marked as latest_released""" + os_matrix = [ + {"rhel-8-10": {"os_version": "8.10", "image_name": "rhel-8.10.qcow2"}}, + {"rhel-9-6": {"os_version": "9.6", "image_name": "rhel-9.6.qcow2", "latest_released": True}}, + ] + + result = generate_latest_os_dict(os_matrix=os_matrix) + + assert result == {"os_version": "9.6", "image_name": "rhel-9.6.qcow2", "latest_released": True} + + def test_returns_latest_os_dict_from_first_position(self): + """Test finding latest_released when it's in the first matrix entry""" + os_matrix = [ + {"fedora-43": {"os_version": "43", "latest_released": True}}, + {"fedora-42": {"os_version": "42"}}, + ] + + result = generate_latest_os_dict(os_matrix=os_matrix) + + assert result == {"os_version": "43", "latest_released": True} + + def test_returns_latest_os_dict_from_middle_position(self): + """Test finding latest_released when it's in a middle matrix entry""" + os_matrix = [ + {"rhel-7-9": {"os_version": "7.9"}}, + {"rhel-9-6": {"os_version": "9.6", "latest_released": True}}, + {"rhel-8-10": {"os_version": "8.10"}}, + ] + + result = generate_latest_os_dict(os_matrix=os_matrix) + + assert result == {"os_version": "9.6", "latest_released": True} + + def test_raises_error_when_no_latest_released(self): + """Test raising OsDictNotFoundError when no OS is marked as latest_released""" + os_matrix = [ + {"rhel-8-10": {"os_version": "8.10"}}, + {"rhel-9-5": {"os_version": "9.5"}}, + ] + + with pytest.raises(OsDictNotFoundError, match="No OS is marked as 'latest_released'"): + generate_latest_os_dict(os_matrix=os_matrix) + + def test_raises_error_on_empty_list(self): + """Test raising OsDictNotFoundError when os_matrix is empty""" + with pytest.raises(OsDictNotFoundError, match="No OS is marked as 'latest_released'"): + generate_latest_os_dict(os_matrix=[]) + + def test_returns_first_latest_when_multiple_marked(self): + """Test returning the first latest_released when multiple OS dicts are marked""" + os_matrix = [ + {"rhel-8-10": {"os_version": "8.10", "latest_released": True}}, + {"rhel-9-6": {"os_version": "9.6", "latest_released": True}}, + ] + + result = generate_latest_os_dict(os_matrix=os_matrix) + + assert result["os_version"] == "8.10" + + def test_handles_complex_os_matrix_structure(self): + """Test with full OS matrix structure including all fields""" + os_matrix = [ + { + "win-2022": { + "os_version": "2022", + "image_name": "win2022.qcow2", + "image_path": "cnv-tests/windows-images/win2022.qcow2", + "dv_size": "60Gi", + "template_labels": {"os": "win2k22", "workload": "server", "flavor": "medium"}, + "data_source": "win2k22", + } + }, + { + "win-2025": { + "os_version": "2025", + "image_name": "win2k25.qcow2", + "image_path": "cnv-tests/windows-uefi-images/win2k25.qcow2", + "dv_size": "60Gi", + "template_labels": {"os": "win2k25", "workload": "server", "flavor": "medium"}, + "data_source": "win2k25", + "latest_released": True, + } + }, + ] + + result = generate_latest_os_dict(os_matrix=os_matrix) + + assert result["os_version"] == "2025" + assert result["image_name"] == "win2k25.qcow2" + assert result["latest_released"] is True + + def test_handles_latest_released_false_value(self): + """Test that latest_released=False is not treated as latest""" + os_matrix = [ + {"rhel-8-10": {"os_version": "8.10", "latest_released": False}}, + {"rhel-9-6": {"os_version": "9.6", "latest_released": True}}, + ] + + result = generate_latest_os_dict(os_matrix=os_matrix) + + assert result["os_version"] == "9.6" + + def test_raises_error_when_all_latest_released_false(self): + """Test raising OsDictNotFoundError when all latest_released values are False""" + os_matrix = [ + {"rhel-8-10": {"os_version": "8.10", "latest_released": False}}, + {"rhel-9-5": {"os_version": "9.5", "latest_released": False}}, + ] + + with pytest.raises(OsDictNotFoundError, match="No OS is marked as 'latest_released'"): + generate_latest_os_dict(os_matrix=os_matrix) diff --git a/utilities/unittests/test_pytest_utils.py b/utilities/unittests/test_pytest_utils.py index e1a6a18ee4..1c2caee25f 100644 --- a/utilities/unittests/test_pytest_utils.py +++ b/utilities/unittests/test_pytest_utils.py @@ -14,6 +14,7 @@ deploy_run_in_progress_config_map, deploy_run_in_progress_namespace, exit_pytest_execution, + generate_os_matrix_dicts, get_artifactory_server_url, get_base_matrix_name, get_cnv_version_explorer_url, @@ -25,6 +26,7 @@ separator, skip_if_pytest_flags_exists, stop_if_run_in_progress, + update_latest_os_config, ) @@ -1178,3 +1180,552 @@ def test_get_matrix_params_missing_matrix_in_config(self, mock_logger, mock_skip # Should return empty list and log warning (lines 94-96) assert result == [] mock_logger.warning.assert_called_with("test_matrix is missing in config file") + + +class TestGenerateOsMatrixDicts: + """Test cases for generate_os_matrix_dicts function""" + + @pytest.fixture + def sample_rhel_matrix(self): + """Sample RHEL OS matrix for testing""" + return [ + { + "rhel-9-6": { + "os_version": "9.6", + "image_name": "rhel-9.6.qcow2", + "latest_released": True, + } + } + ] + + @pytest.fixture + def sample_fedora_matrix(self): + """Sample Fedora OS matrix for testing""" + return [ + { + "fedora-43": { + "os_version": "43", + "image_name": "fedora-43.qcow2", + "latest_released": True, + } + } + ] + + @pytest.fixture + def sample_instance_type_matrix(self): + """Sample instance type OS matrix for testing""" + return [ + { + "rhel.9": { + "preference": "rhel.9", + "latest_released": True, + } + } + ] + + @patch("utilities.pytest_utils.get_cluster_architecture") + @patch("utilities.pytest_utils.generate_linux_instance_type_os_matrix") + @patch("utilities.pytest_utils.generate_latest_os_dict") + @patch("utilities.pytest_utils.generate_os_matrix_dict") + @patch("utilities.pytest_utils.py_config", new_callable=dict) + def test_generate_rhel_os_matrix( + self, + mock_py_config, + mock_generate_os_matrix, + mock_generate_latest, + mock_generate_instance_type, + mock_get_arch, + sample_rhel_matrix, + ): + """Test generating RHEL OS matrix from rhel_os_list""" + mock_get_arch.return_value = "amd64" + mock_generate_os_matrix.return_value = sample_rhel_matrix + mock_generate_latest.return_value = sample_rhel_matrix[0]["rhel-9-6"] + + os_dict = {"rhel_os_list": ["rhel-9-6"]} + generate_os_matrix_dicts(os_dict=os_dict) + + mock_generate_os_matrix.assert_called_once_with(os_name="rhel", supported_operating_systems=["rhel-9-6"]) + mock_generate_latest.assert_called_once() + assert mock_py_config["rhel_os_matrix"] == sample_rhel_matrix + + @patch("utilities.pytest_utils.get_cluster_architecture") + @patch("utilities.pytest_utils.generate_linux_instance_type_os_matrix") + @patch("utilities.pytest_utils.generate_latest_os_dict") + @patch("utilities.pytest_utils.generate_os_matrix_dict") + @patch("utilities.pytest_utils.py_config", new_callable=dict) + def test_generate_fedora_os_matrix( + self, + mock_py_config, + mock_generate_os_matrix, + mock_generate_latest, + mock_generate_instance_type, + mock_get_arch, + sample_fedora_matrix, + ): + """Test generating Fedora OS matrix from fedora_os_list""" + mock_get_arch.return_value = "amd64" + mock_generate_os_matrix.return_value = sample_fedora_matrix + mock_generate_latest.return_value = sample_fedora_matrix[0]["fedora-43"] + + os_dict = {"fedora_os_list": ["fedora-43"]} + generate_os_matrix_dicts(os_dict=os_dict) + + mock_generate_os_matrix.assert_called_once_with(os_name="fedora", supported_operating_systems=["fedora-43"]) + assert mock_py_config["fedora_os_matrix"] == sample_fedora_matrix + + @patch("utilities.pytest_utils.get_cluster_architecture") + @patch("utilities.pytest_utils.generate_linux_instance_type_os_matrix") + @patch("utilities.pytest_utils.generate_latest_os_dict") + @patch("utilities.pytest_utils.generate_os_matrix_dict") + @patch("utilities.pytest_utils.py_config", new_callable=dict) + def test_generate_centos_os_matrix( + self, + mock_py_config, + mock_generate_os_matrix, + mock_generate_latest, + mock_generate_instance_type, + mock_get_arch, + ): + """Test generating CentOS OS matrix from centos_os_list""" + mock_get_arch.return_value = "amd64" + sample_centos_matrix = [{"centos-stream-9": {"os_version": "9", "latest_released": True}}] + mock_generate_os_matrix.return_value = sample_centos_matrix + mock_generate_latest.return_value = sample_centos_matrix[0]["centos-stream-9"] + + os_dict = {"centos_os_list": ["centos-stream-9"]} + generate_os_matrix_dicts(os_dict=os_dict) + + mock_generate_os_matrix.assert_called_once_with( + os_name="centos", supported_operating_systems=["centos-stream-9"] + ) + assert mock_py_config["centos_os_matrix"] == sample_centos_matrix + + @patch("utilities.pytest_utils.get_cluster_architecture") + @patch("utilities.pytest_utils.generate_linux_instance_type_os_matrix") + @patch("utilities.pytest_utils.generate_latest_os_dict") + @patch("utilities.pytest_utils.generate_os_matrix_dict") + @patch("utilities.pytest_utils.py_config", new_callable=dict) + def test_generate_windows_os_matrix( + self, + mock_py_config, + mock_generate_os_matrix, + mock_generate_latest, + mock_generate_instance_type, + mock_get_arch, + ): + """Test generating Windows OS matrix from windows_os_list""" + mock_get_arch.return_value = "amd64" + sample_windows_matrix = [{"win-10": {"os_version": "10", "latest_released": True}}] + mock_generate_os_matrix.return_value = sample_windows_matrix + mock_generate_latest.return_value = sample_windows_matrix[0]["win-10"] + + os_dict = {"windows_os_list": ["win-10"]} + generate_os_matrix_dicts(os_dict=os_dict) + + mock_generate_os_matrix.assert_called_once_with(os_name="windows", supported_operating_systems=["win-10"]) + assert mock_py_config["windows_os_matrix"] == sample_windows_matrix + + @patch("utilities.pytest_utils.get_cluster_architecture") + @patch("utilities.pytest_utils.generate_linux_instance_type_os_matrix") + @patch("utilities.pytest_utils.generate_latest_os_dict") + @patch("utilities.pytest_utils.generate_os_matrix_dict") + @patch("utilities.pytest_utils.py_config", new_callable=dict) + def test_generate_instance_type_rhel_matrix_amd64( + self, + mock_py_config, + mock_generate_os_matrix, + mock_generate_latest, + mock_generate_instance_type, + mock_get_arch, + sample_instance_type_matrix, + ): + """Test generating instance type RHEL matrix on amd64 architecture""" + mock_get_arch.return_value = "amd64" + mock_generate_instance_type.return_value = sample_instance_type_matrix + mock_generate_latest.return_value = sample_instance_type_matrix[0]["rhel.9"] + + os_dict = {"instance_type_rhel_os_list": ["rhel.9"]} + generate_os_matrix_dicts(os_dict=os_dict) + + # On amd64, arch_suffix should be None + mock_generate_instance_type.assert_called_once_with(os_name="rhel", preferences=["rhel.9"], arch_suffix=None) + assert mock_py_config["instance_type_rhel_os_matrix"] == sample_instance_type_matrix + + @patch("utilities.pytest_utils.get_cluster_architecture") + @patch("utilities.pytest_utils.generate_linux_instance_type_os_matrix") + @patch("utilities.pytest_utils.generate_latest_os_dict") + @patch("utilities.pytest_utils.generate_os_matrix_dict") + @patch("utilities.pytest_utils.py_config", new_callable=dict) + def test_generate_instance_type_rhel_matrix_arm64( + self, + mock_py_config, + mock_generate_os_matrix, + mock_generate_latest, + mock_generate_instance_type, + mock_get_arch, + sample_instance_type_matrix, + ): + """Test generating instance type RHEL matrix on arm64 architecture""" + mock_get_arch.return_value = "arm64" + mock_generate_instance_type.return_value = sample_instance_type_matrix + mock_generate_latest.return_value = sample_instance_type_matrix[0]["rhel.9"] + + os_dict = {"instance_type_rhel_os_list": ["rhel.9"]} + generate_os_matrix_dicts(os_dict=os_dict) + + # On arm64, arch_suffix should be "arm64" + mock_generate_instance_type.assert_called_once_with(os_name="rhel", preferences=["rhel.9"], arch_suffix="arm64") + + @patch("utilities.pytest_utils.get_cluster_architecture") + @patch("utilities.pytest_utils.generate_linux_instance_type_os_matrix") + @patch("utilities.pytest_utils.generate_latest_os_dict") + @patch("utilities.pytest_utils.generate_os_matrix_dict") + @patch("utilities.pytest_utils.py_config", new_callable=dict) + def test_generate_instance_type_fedora_matrix( + self, + mock_py_config, + mock_generate_os_matrix, + mock_generate_latest, + mock_generate_instance_type, + mock_get_arch, + ): + """Test generating instance type Fedora matrix""" + mock_get_arch.return_value = "amd64" + sample_fedora_instance_type = [{"fedora": {"preference": "fedora"}}] + mock_generate_instance_type.return_value = sample_fedora_instance_type + + os_dict = {"instance_type_fedora_os_list": ["fedora"]} + generate_os_matrix_dicts(os_dict=os_dict) + + mock_generate_instance_type.assert_called_once_with(os_name="fedora", preferences=["fedora"], arch_suffix=None) + assert mock_py_config["instance_type_fedora_os_matrix"] == sample_fedora_instance_type + + @patch("utilities.pytest_utils.get_cluster_architecture") + @patch("utilities.pytest_utils.generate_linux_instance_type_os_matrix") + @patch("utilities.pytest_utils.generate_latest_os_dict") + @patch("utilities.pytest_utils.generate_os_matrix_dict") + @patch("utilities.pytest_utils.py_config", new_callable=dict) + def test_generate_instance_type_centos_matrix( + self, + mock_py_config, + mock_generate_os_matrix, + mock_generate_latest, + mock_generate_instance_type, + mock_get_arch, + ): + """Test generating instance type CentOS matrix""" + mock_get_arch.return_value = "amd64" + sample_centos_instance_type = [{"centos.stream9": {"preference": "centos.stream9"}}] + mock_generate_instance_type.return_value = sample_centos_instance_type + + os_dict = {"instance_type_centos_os_list": ["centos.stream9"]} + generate_os_matrix_dicts(os_dict=os_dict) + + mock_generate_instance_type.assert_called_once_with( + os_name="centos", preferences=["centos.stream9"], arch_suffix=None + ) + assert mock_py_config["instance_type_centos_os_matrix"] == sample_centos_instance_type + + @patch("utilities.pytest_utils.get_cluster_architecture") + @patch("utilities.pytest_utils.generate_linux_instance_type_os_matrix") + @patch("utilities.pytest_utils.generate_latest_os_dict") + @patch("utilities.pytest_utils.generate_os_matrix_dict") + @patch("utilities.pytest_utils.py_config", new_callable=dict) + def test_empty_os_dict_does_nothing( + self, + mock_py_config, + mock_generate_os_matrix, + mock_generate_latest, + mock_generate_instance_type, + mock_get_arch, + ): + """Test that empty os_dict doesn't call any generation functions""" + mock_get_arch.return_value = "amd64" + + os_dict = {} + generate_os_matrix_dicts(os_dict=os_dict) + + mock_generate_os_matrix.assert_not_called() + mock_generate_instance_type.assert_not_called() + + @patch("utilities.pytest_utils.get_cluster_architecture") + @patch("utilities.pytest_utils.generate_linux_instance_type_os_matrix") + @patch("utilities.pytest_utils.generate_latest_os_dict") + @patch("utilities.pytest_utils.generate_os_matrix_dict") + @patch("utilities.pytest_utils.py_config", new_callable=dict) + def test_generate_multiple_os_matrices( + self, + mock_py_config, + mock_generate_os_matrix, + mock_generate_latest, + mock_generate_instance_type, + mock_get_arch, + sample_rhel_matrix, + sample_fedora_matrix, + ): + """Test generating multiple OS matrices in a single call""" + mock_get_arch.return_value = "amd64" + mock_generate_os_matrix.side_effect = [sample_rhel_matrix, sample_fedora_matrix] + mock_generate_latest.side_effect = [ + sample_rhel_matrix[0]["rhel-9-6"], + sample_fedora_matrix[0]["fedora-43"], + ] + + os_dict = { + "rhel_os_list": ["rhel-9-6"], + "fedora_os_list": ["fedora-43"], + } + generate_os_matrix_dicts(os_dict=os_dict) + + assert mock_generate_os_matrix.call_count == 2 + assert mock_py_config["rhel_os_matrix"] == sample_rhel_matrix + assert mock_py_config["fedora_os_matrix"] == sample_fedora_matrix + + @patch("utilities.pytest_utils.get_cluster_architecture") + @patch("utilities.pytest_utils.generate_linux_instance_type_os_matrix") + @patch("utilities.pytest_utils.generate_latest_os_dict") + @patch("utilities.pytest_utils.generate_os_matrix_dict") + @patch("utilities.pytest_utils.py_config", new_callable=dict) + def test_generate_instance_type_centos_matrix_s390x( + self, + mock_py_config, + mock_generate_os_matrix, + mock_generate_latest, + mock_generate_instance_type, + mock_get_arch, + ): + """Test that CentOS instance type on s390x uses None for arch_suffix""" + mock_get_arch.return_value = "s390x" + sample_centos_instance_type = [{"centos.stream9": {"preference": "centos.stream9"}}] + mock_generate_instance_type.return_value = sample_centos_instance_type + + os_dict = {"instance_type_centos_os_list": ["centos.stream9"]} + generate_os_matrix_dicts(os_dict=os_dict) + + mock_generate_instance_type.assert_called_once_with( + os_name="centos", preferences=["centos.stream9"], arch_suffix=None + ) + + @patch("utilities.pytest_utils.get_cluster_architecture") + @patch("utilities.pytest_utils.generate_linux_instance_type_os_matrix") + @patch("utilities.pytest_utils.generate_latest_os_dict") + @patch("utilities.pytest_utils.generate_os_matrix_dict") + @patch("utilities.pytest_utils.py_config", new_callable=dict) + def test_generate_instance_type_rhel_matrix_s390x( + self, + mock_py_config, + mock_generate_os_matrix, + mock_generate_latest, + mock_generate_instance_type, + mock_get_arch, + sample_instance_type_matrix, + ): + """Test generating instance type RHEL matrix on s390x architecture""" + mock_get_arch.return_value = "s390x" + mock_generate_instance_type.return_value = sample_instance_type_matrix + mock_generate_latest.return_value = sample_instance_type_matrix[0]["rhel.9"] + + os_dict = {"instance_type_rhel_os_list": ["rhel.9"]} + generate_os_matrix_dicts(os_dict=os_dict) + + mock_generate_instance_type.assert_called_once_with(os_name="rhel", preferences=["rhel.9"], arch_suffix="s390x") + + @patch("utilities.pytest_utils.get_cluster_architecture") + @patch("utilities.pytest_utils.generate_linux_instance_type_os_matrix") + @patch("utilities.pytest_utils.generate_latest_os_dict") + @patch("utilities.pytest_utils.generate_os_matrix_dict") + @patch("utilities.pytest_utils.py_config", new_callable=dict) + def test_sets_latest_rhel_os_dict( + self, + mock_py_config, + mock_generate_os_matrix, + mock_generate_latest, + mock_generate_instance_type, + mock_get_arch, + sample_rhel_matrix, + ): + """Test that latest_rhel_os_dict is populated correctly""" + mock_get_arch.return_value = "amd64" + mock_generate_os_matrix.return_value = sample_rhel_matrix + expected_latest = {"os_version": "9.6", "image_name": "rhel-9.6.qcow2", "latest_released": True} + mock_generate_latest.return_value = expected_latest + + os_dict = {"rhel_os_list": ["rhel-9-6"]} + generate_os_matrix_dicts(os_dict=os_dict) + + assert mock_py_config["latest_rhel_os_dict"] == expected_latest + + @patch("utilities.pytest_utils.get_cluster_architecture") + @patch("utilities.pytest_utils.generate_linux_instance_type_os_matrix") + @patch("utilities.pytest_utils.generate_latest_os_dict") + @patch("utilities.pytest_utils.generate_os_matrix_dict") + @patch("utilities.pytest_utils.py_config", new_callable=dict) + def test_sets_latest_instance_type_rhel_os_dict( + self, + mock_py_config, + mock_generate_os_matrix, + mock_generate_latest, + mock_generate_instance_type, + mock_get_arch, + sample_instance_type_matrix, + ): + """Test that latest_instance_type_rhel_os_dict is populated correctly""" + mock_get_arch.return_value = "amd64" + mock_generate_instance_type.return_value = sample_instance_type_matrix + expected_latest = {"preference": "rhel.9", "latest_released": True} + mock_generate_latest.return_value = expected_latest + + os_dict = {"instance_type_rhel_os_list": ["rhel.9"]} + generate_os_matrix_dicts(os_dict=os_dict) + + assert mock_py_config["latest_instance_type_rhel_os_dict"] == expected_latest + + +class TestUpdateLatestOsConfig: + """Test cases for update_latest_os_config function""" + + @patch("utilities.pytest_utils.py_config", new_callable=dict) + def test_saves_system_windows_os_matrix(self, mock_py_config): + """Test that windows_os_matrix is saved to system_windows_os_matrix""" + mock_session_config = MagicMock() + mock_session_config.getoption.return_value = False + windows_matrix = [{"win-10": {"os_version": "10"}}] + mock_py_config["windows_os_matrix"] = windows_matrix + + update_latest_os_config(session_config=mock_session_config) + + assert mock_py_config["system_windows_os_matrix"] == windows_matrix + + @patch("utilities.pytest_utils.py_config", new_callable=dict) + def test_saves_system_rhel_os_matrix(self, mock_py_config): + """Test that rhel_os_matrix is saved to system_rhel_os_matrix""" + mock_session_config = MagicMock() + mock_session_config.getoption.return_value = False + rhel_matrix = [{"rhel-9-6": {"os_version": "9.6"}}] + mock_py_config["rhel_os_matrix"] = rhel_matrix + + update_latest_os_config(session_config=mock_session_config) + + assert mock_py_config["system_rhel_os_matrix"] == rhel_matrix + + @patch("utilities.pytest_utils.py_config", new_callable=dict) + def test_updates_rhel_matrix_with_latest_rhel_option(self, mock_py_config): + """Test updating rhel_os_matrix when latest_rhel option is set""" + mock_session_config = MagicMock() + mock_session_config.getoption.side_effect = lambda opt: opt == "latest_rhel" + mock_py_config["rhel_os_matrix"] = [ + {"rhel-8-10": {"os_version": "8.10"}}, + {"rhel-9-6": {"os_version": "9.6", "latest_released": True}}, + ] + mock_py_config["latest_rhel_os_dict"] = {"os_version": "9.6", "latest_released": True} + mock_py_config["latest_instance_type_rhel_os_dict"] = {"preference": "rhel.9", "latest_released": True} + + update_latest_os_config(session_config=mock_session_config) + + assert mock_py_config["rhel_os_matrix"] == [{"rhel.9.6": {"os_version": "9.6", "latest_released": True}}] + assert mock_py_config["instance_type_rhel_os_matrix"] == [ + {"rhel.9": {"preference": "rhel.9", "latest_released": True}} + ] + + @patch("utilities.pytest_utils.py_config", new_callable=dict) + def test_updates_windows_matrix_with_latest_windows_option(self, mock_py_config): + """Test updating windows_os_matrix when latest_windows option is set""" + mock_session_config = MagicMock() + mock_session_config.getoption.side_effect = lambda opt: opt == "latest_windows" + mock_py_config["windows_os_matrix"] = [ + {"win-10": {"os_version": "10"}}, + {"win-2025": {"os_version": "2025", "latest_released": True}}, + ] + mock_py_config["latest_windows_os_dict"] = {"os_version": "2025", "latest_released": True} + + update_latest_os_config(session_config=mock_session_config) + + assert mock_py_config["windows_os_matrix"] == [ + {"windows.2025": {"os_version": "2025", "latest_released": True}} + ] + + @patch("utilities.pytest_utils.py_config", new_callable=dict) + def test_updates_centos_matrix_with_latest_centos_option(self, mock_py_config): + """Test updating centos_os_matrix when latest_centos option is set""" + mock_session_config = MagicMock() + mock_session_config.getoption.side_effect = lambda opt: opt == "latest_centos" + mock_py_config["centos_os_matrix"] = [{"centos-stream-9": {"os_version": "9", "latest_released": True}}] + mock_py_config["latest_centos_os_dict"] = {"os_version": "9", "latest_released": True} + + update_latest_os_config(session_config=mock_session_config) + + assert mock_py_config["centos_os_matrix"] == [{"centos-stream.9": {"os_version": "9", "latest_released": True}}] + + @patch("utilities.pytest_utils.py_config", new_callable=dict) + def test_updates_fedora_matrix_with_latest_fedora_option(self, mock_py_config): + """Test updating fedora_os_matrix when latest_fedora option is set""" + mock_session_config = MagicMock() + mock_session_config.getoption.side_effect = lambda opt: opt == "latest_fedora" + mock_py_config["fedora_os_matrix"] = [{"fedora-43": {"os_version": "43", "latest_released": True}}] + mock_py_config["latest_fedora_os_dict"] = {"os_version": "43", "latest_released": True} + + update_latest_os_config(session_config=mock_session_config) + + assert mock_py_config["fedora_os_matrix"] == [{"fedora": {"os_version": "43", "latest_released": True}}] + + @patch("utilities.pytest_utils.py_config", new_callable=dict) + def test_no_update_without_latest_option(self, mock_py_config): + """Test that matrices are not updated when latest_* options are not set""" + mock_session_config = MagicMock() + mock_session_config.getoption.return_value = False + original_rhel_matrix = [{"rhel-8-10": {"os_version": "8.10"}}, {"rhel-9-6": {"os_version": "9.6"}}] + mock_py_config["rhel_os_matrix"] = original_rhel_matrix.copy() + + update_latest_os_config(session_config=mock_session_config) + + assert mock_py_config["rhel_os_matrix"] == original_rhel_matrix + + @patch("utilities.pytest_utils.py_config", new_callable=dict) + def test_latest_rhel_with_missing_latest_dict_uses_defaults(self, mock_py_config): + """Test fallback to default values when latest_rhel_os_dict is missing""" + mock_session_config = MagicMock() + mock_session_config.getoption.side_effect = lambda opt: opt == "latest_rhel" + mock_py_config["rhel_os_matrix"] = [{"rhel-9-6": {"os_version": "9.6"}}] + + update_latest_os_config(session_config=mock_session_config) + + assert mock_py_config["rhel_os_matrix"] == [{"rhel.latest": {}}] + assert mock_py_config["instance_type_rhel_os_matrix"] == [{"rhel.latest": {}}] + + @patch("utilities.pytest_utils.py_config", new_callable=dict) + def test_latest_windows_with_missing_latest_dict_uses_defaults(self, mock_py_config): + """Test fallback to default values when latest_windows_os_dict is missing""" + mock_session_config = MagicMock() + mock_session_config.getoption.side_effect = lambda opt: opt == "latest_windows" + mock_py_config["windows_os_matrix"] = [{"win-10": {"os_version": "10"}}] + + update_latest_os_config(session_config=mock_session_config) + + assert mock_py_config["windows_os_matrix"] == [{"windows.latest": {}}] + + @patch("utilities.pytest_utils.py_config", new_callable=dict) + def test_no_update_when_matrix_is_missing(self, mock_py_config): + """Test that latest_* options are ignored when corresponding matrix is missing""" + mock_session_config = MagicMock() + mock_session_config.getoption.side_effect = lambda opt: opt == "latest_rhel" + + update_latest_os_config(session_config=mock_session_config) + + assert "rhel_os_matrix" not in mock_py_config + + @patch("utilities.pytest_utils.py_config", new_callable=dict) + def test_multiple_latest_options(self, mock_py_config): + """Test handling multiple latest_* options simultaneously""" + mock_session_config = MagicMock() + mock_session_config.getoption.side_effect = lambda opt: opt in ("latest_rhel", "latest_windows") + mock_py_config["rhel_os_matrix"] = [{"rhel-9-6": {"os_version": "9.6"}}] + mock_py_config["latest_rhel_os_dict"] = {"os_version": "9.6"} + mock_py_config["latest_instance_type_rhel_os_dict"] = {"preference": "rhel.9"} + mock_py_config["windows_os_matrix"] = [{"win-2025": {"os_version": "2025"}}] + mock_py_config["latest_windows_os_dict"] = {"os_version": "2025"} + + update_latest_os_config(session_config=mock_session_config) + + assert mock_py_config["rhel_os_matrix"] == [{"rhel.9.6": {"os_version": "9.6"}}] + assert mock_py_config["windows_os_matrix"] == [{"windows.2025": {"os_version": "2025"}}]