mirror of
https://github.com/ivan-usov-org/bec.git
synced 2025-04-22 02:20:02 +02:00
Failed config updates should only lead to config flush if the object initialization fails. If we simply can't connect to the signals, the device should be disabled.
221 lines
8.3 KiB
Python
221 lines
8.3 KiB
Python
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, ServiceConfigError
|
|
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._base_path_recovery = "."
|
|
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(tmp_path, test_config_yaml_file_path, config_file):
|
|
orig_cfg_file = test_config_yaml_file_path
|
|
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.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",
|
|
"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",
|
|
"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={"msg": "test"}
|
|
)
|
|
config_helper.wait_for_config_reply.assert_called_once_with(mock.ANY, timeout=32)
|
|
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={"msg": "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.get.return_value = messages.RequestResponseMessage(
|
|
accepted=True, message={"msg": "test"}
|
|
)
|
|
|
|
res = config_helper.wait_for_config_reply("test")
|
|
assert res == messages.RequestResponseMessage(accepted=True, message={"msg": "test"})
|
|
|
|
|
|
def test_wait_for_config_raises_timeout():
|
|
connector = mock.MagicMock()
|
|
config_helper = ConfigHelper(connector)
|
|
connector.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.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.lrange.return_value = []
|
|
|
|
with pytest.raises(DeviceConfigError):
|
|
config_helper.wait_for_service_response("test", timeout=0.3)
|
|
|
|
|
|
def test_update_base_path_recovery():
|
|
with mock.patch("bec_lib.bec_service.SERVICE_CONFIG") as mock_service_config:
|
|
with mock.patch("bec_lib.config_helper.DeviceConfigWriter") as mock_device_config_writer:
|
|
config = {"log_writer": {"base_path": "./"}}
|
|
mock_service_config.config = {"service_config": config}
|
|
connector = mock.MagicMock()
|
|
config_helper = ConfigHelper(connector)
|
|
dir_path = os.path.join(
|
|
config["log_writer"]["base_path"], "device_configs/recovery_configs"
|
|
)
|
|
instance = mock_device_config_writer.get_recovery_directory
|
|
instance.return_value = dir_path
|
|
config_helper._update_base_path_recovery()
|
|
assert mock_device_config_writer.call_args == mock.call(config["log_writer"])
|
|
mock_service_config.config = {"service_config": {}}
|
|
with pytest.raises(ServiceConfigError):
|
|
config_helper._update_base_path_recovery()
|