import os import shutil from unittest import mock import pytest import yaml import bec_lib from bec_lib import messages from bec_lib.bec_errors import DeviceConfigError from bec_lib.config_helper import ConfigHelper dir_path = os.path.dirname(bec_lib.__file__) def test_load_demo_config(): connector = mock.MagicMock() config_helper = ConfigHelper(connector) with mock.patch.object(config_helper, "update_session_with_file") as mock_update: config_helper.load_demo_config() dirpath = os.path.dirname(bec_lib.__file__) fpath = os.path.join(dirpath, "configs/demo_config.yaml") mock_update.assert_called_once_with(fpath) def test_config_helper_update_session_with_file(): connector = mock.MagicMock() config_helper = ConfigHelper(connector) with mock.patch.object(config_helper, "send_config_request") as mock_send_config_request: with mock.patch.object( config_helper, "_load_config_from_file" ) as mock_load_config_from_file: mock_load_config_from_file.return_value = {"test": "test"} config_helper.update_session_with_file("test.yaml") mock_send_config_request.assert_called_once_with(action="set", config={"test": "test"}) @pytest.mark.parametrize("config_file", ["test.yaml", "test.yml"]) def test_config_helper_load_config_from_file(config_file, tmp_path): orig_cfg_file = f"{dir_path}/tests/test_config.yaml" test_cfg_file = tmp_path / config_file shutil.copyfile(orig_cfg_file, test_cfg_file) connector = mock.MagicMock() config_helper = ConfigHelper(connector) config = config_helper._load_config_from_file(test_cfg_file) def test_config_helper_save_current_session(): connector = mock.MagicMock() config_helper = ConfigHelper(connector) connector.producer().get.return_value = messages.AvailableResourceMessage( resource=[ { "id": "648c817f67d3c7cd6a354e8e", "createdAt": "2023-06-16T15:36:31.215Z", "createdBy": "unknown user", "name": "pinz", "sessionId": "648c817d67d3c7cd6a354df2", "enabled": True, "readOnly": False, "deviceClass": "SimPositioner", "deviceTags": ["user motors"], "deviceConfig": { "delay": 1, "labels": "pinz", "limits": [-50, 50], "name": "pinz", "speed": 100, "tolerance": 0.01, "update_frequency": 400, }, "readoutPriority": "baseline", "onFailure": "retry", }, { "id": "648c817f67d3c7cd6a354ec5", "createdAt": "2023-06-16T15:36:31.764Z", "createdBy": "unknown user", "name": "transd", "sessionId": "648c817d67d3c7cd6a354df2", "enabled": True, "readOnly": False, "deviceClass": "SimMonitor", "deviceTags": ["beamline"], "deviceConfig": {"labels": "transd", "name": "transd", "tolerance": 0.5}, "readoutPriority": "monitored", "onFailure": "retry", }, ] ) with mock.patch("builtins.open", mock.mock_open()) as mock_open: config_helper.save_current_session("test.yaml") out_data = { "pinz": { "deviceClass": "SimPositioner", "deviceTags": ["user motors"], "enabled": True, "readOnly": False, "deviceConfig": { "delay": 1, "labels": "pinz", "limits": [-50, 50], "name": "pinz", "speed": 100, "tolerance": 0.01, "update_frequency": 400, }, "readoutPriority": "baseline", "onFailure": "retry", }, "transd": { "deviceClass": "SimMonitor", "deviceTags": ["beamline"], "enabled": True, "readOnly": False, "deviceConfig": {"labels": "transd", "name": "transd", "tolerance": 0.5}, "readoutPriority": "monitored", "onFailure": "retry", }, } mock_open().write.assert_called_once_with(yaml.dump(out_data)) @pytest.fixture def config_helper(): connector = mock.MagicMock() config_helper_inst = ConfigHelper(connector) with mock.patch.object(config_helper_inst, "wait_for_config_reply"): with mock.patch.object(config_helper_inst, "wait_for_service_response"): yield config_helper_inst def test_send_config_request_raises_with_empty_config(config_helper): with pytest.raises(DeviceConfigError): config_helper.send_config_request(action="update") config_helper.wait_for_config_reply.assert_called_once_with(mock.ANY) def test_send_config_request(config_helper): config_helper.send_config_request(action="update", config={"test": "test"}) config_helper.wait_for_config_reply.return_value = messages.RequestResponseMessage( accepted=True, message="test" ) config_helper.wait_for_config_reply.assert_called_once_with(mock.ANY) config_helper.wait_for_service_response.assert_called_once_with(mock.ANY) def test_send_config_request_raises_for_rejected_update(config_helper): config_helper.wait_for_config_reply.return_value = messages.RequestResponseMessage( accepted=False, message="test" ) with pytest.raises(DeviceConfigError): config_helper.send_config_request(action="update", config={"test": "test"}) config_helper.wait_for_config_reply.assert_called_once_with(mock.ANY) def test_wait_for_config_reply(): connector = mock.MagicMock() config_helper = ConfigHelper(connector) connector.producer().get.return_value = messages.RequestResponseMessage( accepted=True, message="test" ) res = config_helper.wait_for_config_reply("test") assert res == messages.RequestResponseMessage(accepted=True, message="test") def test_wait_for_config_raises_timeout(): connector = mock.MagicMock() config_helper = ConfigHelper(connector) connector.producer().get.return_value = None with pytest.raises(DeviceConfigError): config_helper.wait_for_config_reply("test", timeout=0.3) def test_wait_for_service_response(): connector = mock.MagicMock() config_helper = ConfigHelper(connector) connector.producer().lrange.side_effect = [ [], [ messages.ServiceResponseMessage( response={"service": "DeviceServer"}, metadata={"RID": "test"} ), messages.ServiceResponseMessage( response={"service": "ScanServer"}, metadata={"RID": "test"} ), ], ] config_helper.wait_for_service_response("test", timeout=0.3) def test_wait_for_service_response_raises_timeout(): connector = mock.MagicMock() config_helper = ConfigHelper(connector) connector.producer().lrange.return_value = [] with pytest.raises(DeviceConfigError): config_helper.wait_for_service_response("test", timeout=0.3)