Skip to content
14 changes: 8 additions & 6 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -786,13 +786,14 @@ def _update_os_related_config():

py_config[key] = items_list
config_default_storage_class(session=session)
admin_client = utilities.cluster.cache_admin_client()
# Set py_config["servers"] and py_config["os_login_param"]
# Send --tc=server_url:<url> to override servers URL
if not skip_if_pytest_flags_exists(pytest_config=session.config):
py_config["version_explorer_url"] = get_cnv_version_explorer_url(pytest_config=session.config)
if not session.config.getoption("--skip-artifactory-check"):
py_config["server_url"] = py_config["server_url"] or get_artifactory_server_url(
cluster_host_url=utilities.cluster.cache_admin_client().configuration.host
cluster_host_url=admin_client.configuration.host
)
py_config["servers"] = {
name: _server.format(server=py_config["server_url"]) for name, _server in py_config["servers"].items()
Expand All @@ -801,9 +802,9 @@ def _update_os_related_config():

# must be at the end to make sure we create it only after all pytest_sessionstart checks pass.
if not skip_if_pytest_flags_exists(pytest_config=session.config):
stop_if_run_in_progress()
deploy_run_in_progress_namespace()
deploy_run_in_progress_config_map(session=session)
stop_if_run_in_progress(client=admin_client)
deploy_run_in_progress_namespace(client=admin_client)
deploy_run_in_progress_config_map(client=admin_client, session=session)


def pytest_collection_finish(session):
Expand All @@ -815,8 +816,9 @@ def pytest_collection_finish(session):
def pytest_sessionfinish(session, exitstatus):
shutil.rmtree(path=session.config.option.basetemp, ignore_errors=True)
if not skip_if_pytest_flags_exists(pytest_config=session.config):
run_in_progress_config_map().clean_up()
deploy_run_in_progress_namespace().clean_up()
admin_client = utilities.cluster.cache_admin_client()
run_in_progress_config_map(client=admin_client).clean_up()
deploy_run_in_progress_namespace(client=admin_client).clean_up()

reporter = session.config.pluginmanager.get_plugin("terminalreporter")
reporter.summary_stats()
Expand Down
2 changes: 2 additions & 0 deletions utilities/architecture.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ def get_cluster_architecture() -> str:
"""
Returns cluster architecture.

To run in CI, where a cluster is not available, set `OPENSHIFT_VIRTUALIZATION_TEST_IMAGES_ARCH` env variable.

Returns:
str: cluster architecture.

Expand Down
16 changes: 9 additions & 7 deletions utilities/pytest_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import sys

import pytest
from kubernetes.dynamic import DynamicClient
from ocp_resources.config_map import ConfigMap
from ocp_resources.namespace import Namespace
from ocp_resources.resource import ResourceEditor
Expand Down Expand Up @@ -161,8 +162,8 @@ def reorder_early_fixtures(metafunc):
break


def stop_if_run_in_progress():
run_in_progress = run_in_progress_config_map()
def stop_if_run_in_progress(client: DynamicClient) -> None:
run_in_progress = run_in_progress_config_map(client=client)
if run_in_progress.exists:
exit_pytest_execution(
message=f"openshift-virtualization-tests run already in progress: \n{run_in_progress.instance.data}"
Expand All @@ -172,21 +173,22 @@ def stop_if_run_in_progress():
)


def deploy_run_in_progress_namespace():
run_in_progress_namespace = Namespace(name=CNV_TEST_RUN_IN_PROGRESS_NS)
def deploy_run_in_progress_namespace(client: DynamicClient) -> Namespace:
run_in_progress_namespace = Namespace(client=client, name=CNV_TEST_RUN_IN_PROGRESS_NS)
if not run_in_progress_namespace.exists:
run_in_progress_namespace.deploy(wait=True)
run_in_progress_namespace.wait_for_status(status=Namespace.Status.ACTIVE, timeout=TIMEOUT_2MIN)
ResourceEditor({run_in_progress_namespace: {"metadata": {"labels": POD_SECURITY_NAMESPACE_LABELS}}}).update()
return run_in_progress_namespace


def deploy_run_in_progress_config_map(session):
run_in_progress_config_map(session=session).deploy()
def deploy_run_in_progress_config_map(client: DynamicClient, session) -> None:
run_in_progress_config_map(client=client, session=session).deploy(wait=True)


def run_in_progress_config_map(session=None):
def run_in_progress_config_map(client: DynamicClient, session=None) -> ConfigMap:
return ConfigMap(
client=client,
name=CNV_TEST_RUN_IN_PROGRESS,
namespace=CNV_TEST_RUN_IN_PROGRESS_NS,
data=get_current_running_data(session=session) if session else None,
Expand Down
101 changes: 101 additions & 0 deletions utilities/unittests/test_cluster.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Generated using Claude cli

"""Unit tests for cluster module"""

from unittest.mock import MagicMock, patch

from utilities.cluster import cache_admin_client


class TestCacheAdminClient:
"""Test cases for cache_admin_client function"""

@patch("utilities.cluster.get_client")
def test_cache_admin_client_returns_client(self, mock_get_client):
"""Test that cache_admin_client returns a DynamicClient"""
# Clear the cache before testing
cache_admin_client.cache_clear()

mock_client = MagicMock()
mock_get_client.return_value = mock_client

result = cache_admin_client()

assert result == mock_client
mock_get_client.assert_called_once()

@patch("utilities.cluster.get_client")
def test_cache_admin_client_caches_result(self, mock_get_client):
"""Test that cache_admin_client caches the client on repeated calls"""
# Clear the cache before testing
cache_admin_client.cache_clear()

mock_client = MagicMock()
mock_get_client.return_value = mock_client

# Call multiple times
result1 = cache_admin_client()
result2 = cache_admin_client()
result3 = cache_admin_client()

# All results should be the same cached object
assert result1 is result2
assert result2 is result3
# get_client should only be called once due to caching
mock_get_client.assert_called_once()

@patch("utilities.cluster.get_client")
def test_cache_admin_client_cache_clear(self, mock_get_client):
"""Test that cache can be cleared and get_client is called again"""
# Clear the cache before testing
cache_admin_client.cache_clear()

mock_client1 = MagicMock()
mock_client2 = MagicMock()
mock_get_client.side_effect = [mock_client1, mock_client2]

# First call
result1 = cache_admin_client()
assert result1 == mock_client1

# Clear cache
cache_admin_client.cache_clear()

# Second call should get new client
result2 = cache_admin_client()
assert result2 == mock_client2

# get_client should be called twice
assert mock_get_client.call_count == 2

@patch("utilities.cluster.get_client")
def test_cache_admin_client_cache_info(self, mock_get_client):
"""Test that cache_info tracks cache statistics"""
# Clear the cache before testing
cache_admin_client.cache_clear()

mock_client = MagicMock()
mock_get_client.return_value = mock_client

# Check initial cache info
info = cache_admin_client.cache_info()
assert info.hits == 0
assert info.misses == 0

# First call - cache miss
cache_admin_client()
info = cache_admin_client.cache_info()
assert info.hits == 0
assert info.misses == 1

# Second call - cache hit
cache_admin_client()
info = cache_admin_client.cache_info()
assert info.hits == 1
assert info.misses == 1

# Third call - another cache hit
cache_admin_client()
info = cache_admin_client.cache_info()
assert info.hits == 2
assert info.misses == 1
39 changes: 28 additions & 11 deletions utilities/unittests/test_pytest_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -454,9 +454,11 @@ def test_stop_if_run_in_progress_exists(self, mock_exit, mock_config_map):
mock_cm.namespace = "test-namespace"
mock_cm.name = "test-configmap"
mock_config_map.return_value = mock_cm
mock_client = MagicMock()

stop_if_run_in_progress()
stop_if_run_in_progress(client=mock_client)

mock_config_map.assert_called_once_with(client=mock_client)
mock_exit.assert_called_once()
assert "test_user" in mock_exit.call_args[1]["message"]
assert mock_exit.call_args[1]["return_code"] == 100
Expand All @@ -468,9 +470,11 @@ def test_stop_if_run_in_progress_not_exists(self, mock_exit, mock_config_map):
mock_cm = MagicMock()
mock_cm.exists = False
mock_config_map.return_value = mock_cm
mock_client = MagicMock()

stop_if_run_in_progress()
stop_if_run_in_progress(client=mock_client)

mock_config_map.assert_called_once_with(client=mock_client)
mock_exit.assert_not_called()


Expand All @@ -484,10 +488,12 @@ def test_deploy_run_in_progress_namespace_not_exists(self, mock_namespace_class,
mock_namespace = MagicMock()
mock_namespace.exists = False
mock_namespace_class.return_value = mock_namespace
mock_client = MagicMock()

result = deploy_run_in_progress_namespace()
result = deploy_run_in_progress_namespace(client=mock_client)

assert result == mock_namespace
mock_namespace_class.assert_called_once_with(client=mock_client, name="cnv-tests-run-in-progress-ns")
mock_namespace.deploy.assert_called_once_with(wait=True)
mock_namespace.wait_for_status.assert_called_once()
mock_resource_editor.assert_called_once()
Expand All @@ -498,10 +504,12 @@ def test_deploy_run_in_progress_namespace_exists(self, mock_namespace_class):
mock_namespace = MagicMock()
mock_namespace.exists = True
mock_namespace_class.return_value = mock_namespace
mock_client = MagicMock()

result = deploy_run_in_progress_namespace()
result = deploy_run_in_progress_namespace(client=mock_client)

assert result == mock_namespace
mock_namespace_class.assert_called_once_with(client=mock_client, name="cnv-tests-run-in-progress-ns")
mock_namespace.deploy.assert_not_called()


Expand All @@ -514,11 +522,12 @@ def test_deploy_run_in_progress_config_map(self, mock_config_map):
mock_cm = MagicMock()
mock_config_map.return_value = mock_cm
mock_session = MagicMock()
mock_client = MagicMock()

deploy_run_in_progress_config_map(mock_session)
deploy_run_in_progress_config_map(client=mock_client, session=mock_session)

mock_config_map.assert_called_once_with(session=mock_session)
mock_cm.deploy.assert_called_once()
mock_config_map.assert_called_once_with(client=mock_client, session=mock_session)
mock_cm.deploy.assert_called_once_with(wait=True)


class TestRunInProgressConfigMap:
Expand All @@ -533,26 +542,34 @@ def test_run_in_progress_config_map_with_session(self, mock_config_map_class, mo
mock_get_data.return_value = mock_data
mock_cm = MagicMock()
mock_config_map_class.return_value = mock_cm
mock_client = MagicMock()

result = run_in_progress_config_map(mock_session)
result = run_in_progress_config_map(client=mock_client, session=mock_session)

assert result == mock_cm
mock_get_data.assert_called_once_with(session=mock_session)
mock_config_map_class.assert_called_once_with(
name="cnv-tests-run-in-progress", namespace="cnv-tests-run-in-progress-ns", data=mock_data
client=mock_client,
name="cnv-tests-run-in-progress",
namespace="cnv-tests-run-in-progress-ns",
data=mock_data,
)

@patch("utilities.pytest_utils.ConfigMap")
def test_run_in_progress_config_map_without_session(self, mock_config_map_class):
"""Test creating config map without session data"""
mock_cm = MagicMock()
mock_config_map_class.return_value = mock_cm
mock_client = MagicMock()

result = run_in_progress_config_map(None)
result = run_in_progress_config_map(client=mock_client, session=None)

assert result == mock_cm
mock_config_map_class.assert_called_once_with(
name="cnv-tests-run-in-progress", namespace="cnv-tests-run-in-progress-ns", data=None
client=mock_client,
name="cnv-tests-run-in-progress",
namespace="cnv-tests-run-in-progress-ns",
data=None,
)


Expand Down
Loading