From: Guillaume Abrioux Date: Tue, 3 Feb 2026 15:26:16 +0000 (+0100) Subject: node-proxy: add unit tests X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=9797d9c44f837e498099b2a083da6ef2a5b3224c;p=ceph.git node-proxy: add unit tests This adds some unit tests. Fixes: https://tracker.ceph.com/issues/74749 Signed-off-by: Guillaume Abrioux --- diff --git a/src/ceph-node-proxy/setup.py b/src/ceph-node-proxy/setup.py index 13b69433ad6..ba562f0ec3f 100644 --- a/src/ceph-node-proxy/setup.py +++ b/src/ceph-node-proxy/setup.py @@ -19,6 +19,7 @@ setup( 'python-common#egg=ceph-1.0.0')])], tests_require=[ 'pytest >=2.1.3', + 'PyYAML', 'tox', 'ceph', ], diff --git a/src/ceph-node-proxy/tests/__init__.py b/src/ceph-node-proxy/tests/__init__.py new file mode 100644 index 00000000000..daf85dc9dc3 --- /dev/null +++ b/src/ceph-node-proxy/tests/__init__.py @@ -0,0 +1 @@ +# Tests for ceph-node-proxy diff --git a/src/ceph-node-proxy/tests/test_baseredfishsystem.py b/src/ceph-node-proxy/tests/test_baseredfishsystem.py new file mode 100644 index 00000000000..e3d3382b1c8 --- /dev/null +++ b/src/ceph-node-proxy/tests/test_baseredfishsystem.py @@ -0,0 +1,234 @@ +from unittest.mock import MagicMock, patch + +import pytest + +from ceph_node_proxy.baseredfishsystem import BaseRedfishSystem +from ceph_node_proxy.redfish import ComponentUpdateSpec + + +@pytest.fixture +def mock_client(): + return MagicMock() + + +@pytest.fixture +def mock_endpoints(): + return MagicMock() + + +@pytest.fixture +def system(mock_client, mock_endpoints): + with ( + patch( + "ceph_node_proxy.baseredfishsystem.RedFishClient", return_value=mock_client + ), + patch( + "ceph_node_proxy.baseredfishsystem.EndpointMgr", return_value=mock_endpoints + ), + ): + return BaseRedfishSystem( + host="testhost", + port="443", + username="user", + password="secret", + config={}, + ) + + +class TestBaseRedfishSystemInit: + def test_init_sets_client_and_endpoints(self, system, mock_client, mock_endpoints): + assert system.client is mock_client + assert system.endpoints is mock_endpoints + + def test_init_default_component_list(self, system): + assert "memory" in system.component_list + assert "power" in system.component_list + assert "network" in system.component_list + assert "processors" in system.component_list + assert "storage" in system.component_list + assert "firmwares" in system.component_list + assert "fans" in system.component_list + + def test_init_update_funcs_populated(self, system): + # Should have one callable per component that has _update_ + assert len(system.update_funcs) >= 6 + + def test_init_custom_component_list(self, mock_client, mock_endpoints): + with ( + patch( + "ceph_node_proxy.baseredfishsystem.RedFishClient", + return_value=mock_client, + ), + patch( + "ceph_node_proxy.baseredfishsystem.EndpointMgr", + return_value=mock_endpoints, + ), + ): + sys = BaseRedfishSystem( + host="h", + port="443", + username="u", + password="p", + config={}, + component_list=["memory", "network"], + ) + assert sys.component_list == ["memory", "network"] + assert len(sys.update_funcs) == 2 + + +class TestBaseRedfishSystemGetters: + def test_get_sn_empty(self, system): + assert system.get_sn() == "" + + def test_get_sn_from_sys(self, system): + system._sys["SKU"] = "ABC123" + assert system.get_sn() == "ABC123" + + def test_get_memory_empty(self, system): + assert system.get_memory() == {} + + def test_get_memory_from_sys(self, system): + system._sys["memory"] = {"slot1": {"capacity_mib": 16384}} + assert system.get_memory() == {"slot1": {"capacity_mib": 16384}} + + def test_get_processors_empty(self, system): + assert system.get_processors() == {} + + def test_get_network_empty(self, system): + assert system.get_network() == {} + + def test_get_storage_empty(self, system): + assert system.get_storage() == {} + + def test_get_power_empty(self, system): + assert system.get_power() == {} + + def test_get_fans_empty(self, system): + assert system.get_fans() == {} + + def test_get_firmwares_empty(self, system): + assert system.get_firmwares() == {} + + def test_get_status_empty(self, system): + assert system.get_status() == {} + + +class TestBaseRedfishSystemGetSystem: + def test_get_system_structure(self, system): + system._sys["SKU"] = "SN1" + system._sys["memory"] = {} + system._sys["processors"] = {} + system._sys["network"] = {} + system._sys["storage"] = {} + system._sys["power"] = {} + system._sys["fans"] = {} + system._sys["firmwares"] = {} + result = system.get_system() + assert "host" in result + assert "sn" in result + assert result["sn"] == "SN1" + assert "status" in result + assert result["status"]["memory"] == {} + assert result["status"]["processors"] == {} + assert result["status"]["network"] == {} + assert result["status"]["storage"] == {} + assert result["status"]["power"] == {} + assert result["status"]["fans"] == {} + assert "firmwares" in result + + +class TestBaseRedfishSystemGetUpdateSpec: + + def test_get_update_spec_network(self, system): + spec = system.get_update_spec("network") + assert isinstance(spec, ComponentUpdateSpec) + assert spec.collection == "systems" + assert spec.path == "EthernetInterfaces" + assert "Name" in spec.fields + assert spec.attribute is None + + def test_get_update_spec_memory(self, system): + spec = system.get_update_spec("memory") + assert spec.collection == "systems" + assert spec.path == "Memory" + assert "CapacityMiB" in spec.fields + + def test_get_update_spec_power(self, system): + spec = system.get_update_spec("power") + assert spec.collection == "chassis" + assert "PowerSubsystem" in spec.path + assert spec.attribute is None + + def test_get_update_spec_fans(self, system): + spec = system.get_update_spec("fans") + assert spec.collection == "chassis" + assert spec.path == "Thermal" + assert spec.attribute == "Fans" + + def test_get_component_spec_overrides_empty(self, system): + assert system.get_component_spec_overrides() == {} + + +class TestBaseRedfishSystemFlush: + def test_flush_clears_state(self, system): + system._system = {"x": 1} + system.previous_data = {"y": 2} + system.data_ready = True + system.flush() + assert system._system == {} + assert system.previous_data == {} + assert system.data_ready is False + + +class TestBaseRedfishSystemUpdate: + def test_update_calls_update_component(self, system): + with patch("ceph_node_proxy.baseredfishsystem.update_component") as mock_update: + system.update( + collection="systems", + component="memory", + path="Memory", + fields=BaseRedfishSystem.MEMORY_FIELDS, + attribute=None, + ) + mock_update.assert_called_once() + call_kw = mock_update.call_args[1] + assert call_kw.get("attribute") is None + assert mock_update.call_args[0][1] == "systems" + assert mock_update.call_args[0][2] == "memory" + assert mock_update.call_args[0][3] == "Memory" + + +class TestBaseRedfishSystemNotImplemented: + @pytest.mark.parametrize( + "method,args", + [ + ("device_led_on", ("disk1",)), + ("device_led_off", ("disk1",)), + ("chassis_led_on", ()), + ("chassis_led_off", ()), + ("get_device_led", ("disk1",)), + ("set_device_led", ("disk1", {"on": True})), + ("get_chassis_led", ()), + ("set_chassis_led", ({"state": "on"},)), + ("shutdown_host", (False,)), + ("powercycle", ()), + ], + ) + def test_not_implemented(self, system, method, args): + with pytest.raises(NotImplementedError): + getattr(system, method)(*args) + + +class TestBaseRedfishSystemComponentSpecs: + def test_component_specs_has_expected_keys(self): + assert "network" in BaseRedfishSystem.COMPONENT_SPECS + assert "processors" in BaseRedfishSystem.COMPONENT_SPECS + assert "memory" in BaseRedfishSystem.COMPONENT_SPECS + assert "power" in BaseRedfishSystem.COMPONENT_SPECS + assert "fans" in BaseRedfishSystem.COMPONENT_SPECS + assert "firmwares" in BaseRedfishSystem.COMPONENT_SPECS + + def test_field_lists_non_empty(self): + assert len(BaseRedfishSystem.NETWORK_FIELDS) > 0 + assert len(BaseRedfishSystem.MEMORY_FIELDS) > 0 + assert len(BaseRedfishSystem.POWER_FIELDS) > 0 diff --git a/src/ceph-node-proxy/tox.ini b/src/ceph-node-proxy/tox.ini index 5e4acb62d4c..6ca6fe55bdb 100644 --- a/src/ceph-node-proxy/tox.ini +++ b/src/ceph-node-proxy/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = mypy,flake8,isort,black +envlist = mypy,flake8,isort,black,pytest minversion = 3.0 skipsdist = true @@ -28,3 +28,10 @@ commands = isort --profile black --check-only --diff ceph_node_proxy description = Check code formatting with black deps = black >= 23.0 commands = black --check ceph_node_proxy + +[testenv:pytest] +description = Run unit tests with pytest +deps = + pytest >= 7.0 + PyYAML +commands = pytest tests/ -v