fixed config service to leverage validation_timer properly, added unit tests for config service base class
This commit is contained in:
parent
80073dd8c8
commit
09aa882017
2 changed files with 310 additions and 2 deletions
|
@ -140,7 +140,7 @@ class ConfigService(abc.ABC):
|
||||||
self.run_startup(wait)
|
self.run_startup(wait)
|
||||||
if not wait:
|
if not wait:
|
||||||
if self.validation_mode == ConfigServiceMode.TIMER:
|
if self.validation_mode == ConfigServiceMode.TIMER:
|
||||||
time.sleep(self.validation_timer)
|
self.wait_validation()
|
||||||
else:
|
else:
|
||||||
self.run_validation()
|
self.run_validation()
|
||||||
|
|
||||||
|
@ -275,6 +275,14 @@ class ConfigService(abc.ABC):
|
||||||
f"node({self.node.name}) service({self.name}) failed startup: {e}"
|
f"node({self.node.name}) service({self.name}) failed startup: {e}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def wait_validation(self) -> None:
|
||||||
|
"""
|
||||||
|
Waits for a period of time to consider service started successfully.
|
||||||
|
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
time.sleep(self.validation_timer)
|
||||||
|
|
||||||
def run_validation(self) -> None:
|
def run_validation(self) -> None:
|
||||||
"""
|
"""
|
||||||
Runs validation commands for service on node.
|
Runs validation commands for service on node.
|
||||||
|
@ -298,7 +306,7 @@ class ConfigService(abc.ABC):
|
||||||
)
|
)
|
||||||
time.sleep(self.validation_period)
|
time.sleep(self.validation_period)
|
||||||
|
|
||||||
if cmds and time.monotonic() - start > 0:
|
if cmds and time.monotonic() - start > self.validation_timer:
|
||||||
raise ConfigServiceBootError(
|
raise ConfigServiceBootError(
|
||||||
f"node({self.node.name}) service({self.name}) failed to validate"
|
f"node({self.node.name}) service({self.name}) failed to validate"
|
||||||
)
|
)
|
||||||
|
|
300
daemon/tests/test_config_services.py
Normal file
300
daemon/tests/test_config_services.py
Normal file
|
@ -0,0 +1,300 @@
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from core.config import Configuration
|
||||||
|
from core.configservice.base import (
|
||||||
|
ConfigService,
|
||||||
|
ConfigServiceBootError,
|
||||||
|
ConfigServiceMode,
|
||||||
|
)
|
||||||
|
from core.emulator.enumerations import ConfigDataTypes
|
||||||
|
from core.errors import CoreCommandError, CoreError
|
||||||
|
|
||||||
|
TEMPLATE_TEXT = "echo hello"
|
||||||
|
|
||||||
|
|
||||||
|
class MyService(ConfigService):
|
||||||
|
name = "MyService"
|
||||||
|
group = "MyGroup"
|
||||||
|
directories = ["/usr/local/lib"]
|
||||||
|
files = ["test.sh"]
|
||||||
|
executables = []
|
||||||
|
dependencies = []
|
||||||
|
startup = [f"sh {files[0]}"]
|
||||||
|
validate = [f"pidof {files[0]}"]
|
||||||
|
shutdown = [f"pkill {files[0]}"]
|
||||||
|
validation_mode = ConfigServiceMode.BLOCKING
|
||||||
|
default_configs = [
|
||||||
|
Configuration(_id="value1", _type=ConfigDataTypes.STRING, label="Text"),
|
||||||
|
Configuration(_id="value2", _type=ConfigDataTypes.BOOL, label="Boolean"),
|
||||||
|
Configuration(
|
||||||
|
_id="value3",
|
||||||
|
_type=ConfigDataTypes.STRING,
|
||||||
|
label="Multiple Choice",
|
||||||
|
options=["value1", "value2", "value3"],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
modes = {
|
||||||
|
"mode1": {"value1": "value1", "value2": "0", "value3": "value2"},
|
||||||
|
"mode2": {"value1": "value2", "value2": "1", "value3": "value3"},
|
||||||
|
"mode3": {"value1": "value3", "value2": "0", "value3": "value1"},
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_text_template(self, name: str) -> str:
|
||||||
|
return TEMPLATE_TEXT
|
||||||
|
|
||||||
|
|
||||||
|
class TestConfigServices:
|
||||||
|
def test_set_template(self):
|
||||||
|
# given
|
||||||
|
node = mock.MagicMock()
|
||||||
|
text = "echo custom"
|
||||||
|
service = MyService(node)
|
||||||
|
|
||||||
|
# when
|
||||||
|
service.set_template(MyService.files[0], text)
|
||||||
|
|
||||||
|
# then
|
||||||
|
assert MyService.files[0] in service.custom_templates
|
||||||
|
assert service.custom_templates[MyService.files[0]] == text
|
||||||
|
|
||||||
|
def test_create_directories(self):
|
||||||
|
# given
|
||||||
|
node = mock.MagicMock()
|
||||||
|
service = MyService(node)
|
||||||
|
|
||||||
|
# when
|
||||||
|
service.create_dirs()
|
||||||
|
|
||||||
|
# then
|
||||||
|
node.privatedir.assert_called_with(MyService.directories[0])
|
||||||
|
|
||||||
|
def test_create_files_custom(self):
|
||||||
|
# given
|
||||||
|
node = mock.MagicMock()
|
||||||
|
service = MyService(node)
|
||||||
|
text = "echo custom"
|
||||||
|
service.set_template(MyService.files[0], text)
|
||||||
|
|
||||||
|
# when
|
||||||
|
service.create_files()
|
||||||
|
|
||||||
|
# then
|
||||||
|
node.nodefile.assert_called_with(MyService.files[0], text)
|
||||||
|
|
||||||
|
def test_create_files_text(self):
|
||||||
|
# given
|
||||||
|
node = mock.MagicMock()
|
||||||
|
service = MyService(node)
|
||||||
|
|
||||||
|
# when
|
||||||
|
service.create_files()
|
||||||
|
|
||||||
|
# then
|
||||||
|
node.nodefile.assert_called_with(MyService.files[0], TEMPLATE_TEXT)
|
||||||
|
|
||||||
|
def test_run_startup(self):
|
||||||
|
# given
|
||||||
|
node = mock.MagicMock()
|
||||||
|
wait = True
|
||||||
|
service = MyService(node)
|
||||||
|
|
||||||
|
# when
|
||||||
|
service.run_startup(wait=wait)
|
||||||
|
|
||||||
|
# then
|
||||||
|
node.cmd.assert_called_with(MyService.startup[0], wait=wait)
|
||||||
|
|
||||||
|
def test_run_startup_exception(self):
|
||||||
|
# given
|
||||||
|
node = mock.MagicMock()
|
||||||
|
node.cmd.side_effect = CoreCommandError(1, "error")
|
||||||
|
service = MyService(node)
|
||||||
|
|
||||||
|
# when
|
||||||
|
with pytest.raises(ConfigServiceBootError):
|
||||||
|
service.run_startup(wait=True)
|
||||||
|
|
||||||
|
def test_shutdown(self):
|
||||||
|
# given
|
||||||
|
node = mock.MagicMock()
|
||||||
|
service = MyService(node)
|
||||||
|
|
||||||
|
# when
|
||||||
|
service.stop()
|
||||||
|
|
||||||
|
# then
|
||||||
|
node.cmd.assert_called_with(MyService.shutdown[0])
|
||||||
|
|
||||||
|
def test_run_validation(self):
|
||||||
|
# given
|
||||||
|
node = mock.MagicMock()
|
||||||
|
service = MyService(node)
|
||||||
|
|
||||||
|
# when
|
||||||
|
service.run_validation()
|
||||||
|
|
||||||
|
# then
|
||||||
|
node.cmd.assert_called_with(MyService.validate[0])
|
||||||
|
|
||||||
|
def test_run_validation_timer(self):
|
||||||
|
# given
|
||||||
|
node = mock.MagicMock()
|
||||||
|
service = MyService(node)
|
||||||
|
service.validation_mode = ConfigServiceMode.TIMER
|
||||||
|
service.validation_timer = 0
|
||||||
|
|
||||||
|
# when
|
||||||
|
service.run_validation()
|
||||||
|
|
||||||
|
# then
|
||||||
|
node.cmd.assert_called_with(MyService.validate[0])
|
||||||
|
|
||||||
|
def test_run_validation_timer_exception(self):
|
||||||
|
# given
|
||||||
|
node = mock.MagicMock()
|
||||||
|
node.cmd.side_effect = CoreCommandError(1, "error")
|
||||||
|
service = MyService(node)
|
||||||
|
service.validation_mode = ConfigServiceMode.TIMER
|
||||||
|
service.validation_period = 0
|
||||||
|
service.validation_timer = 0
|
||||||
|
|
||||||
|
# when
|
||||||
|
with pytest.raises(ConfigServiceBootError):
|
||||||
|
service.run_validation()
|
||||||
|
|
||||||
|
def test_run_validation_non_blocking(self):
|
||||||
|
# given
|
||||||
|
node = mock.MagicMock()
|
||||||
|
service = MyService(node)
|
||||||
|
service.validation_mode = ConfigServiceMode.NON_BLOCKING
|
||||||
|
service.validation_period = 0
|
||||||
|
service.validation_timer = 0
|
||||||
|
|
||||||
|
# when
|
||||||
|
service.run_validation()
|
||||||
|
|
||||||
|
# then
|
||||||
|
node.cmd.assert_called_with(MyService.validate[0])
|
||||||
|
|
||||||
|
def test_run_validation_non_blocking_exception(self):
|
||||||
|
# given
|
||||||
|
node = mock.MagicMock()
|
||||||
|
node.cmd.side_effect = CoreCommandError(1, "error")
|
||||||
|
service = MyService(node)
|
||||||
|
service.validation_mode = ConfigServiceMode.NON_BLOCKING
|
||||||
|
service.validation_period = 0
|
||||||
|
service.validation_timer = 0
|
||||||
|
|
||||||
|
# when
|
||||||
|
with pytest.raises(ConfigServiceBootError):
|
||||||
|
service.run_validation()
|
||||||
|
|
||||||
|
def test_render_config(self):
|
||||||
|
# given
|
||||||
|
node = mock.MagicMock()
|
||||||
|
service = MyService(node)
|
||||||
|
|
||||||
|
# when
|
||||||
|
config = service.render_config()
|
||||||
|
|
||||||
|
# then
|
||||||
|
assert config == {"value1": "", "value2": "", "value3": ""}
|
||||||
|
|
||||||
|
def test_render_config_custom(self):
|
||||||
|
# given
|
||||||
|
node = mock.MagicMock()
|
||||||
|
service = MyService(node)
|
||||||
|
custom_config = {"value1": "1", "value2": "2", "value3": "3"}
|
||||||
|
service.set_config(custom_config)
|
||||||
|
|
||||||
|
# when
|
||||||
|
config = service.render_config()
|
||||||
|
|
||||||
|
# then
|
||||||
|
assert config == custom_config
|
||||||
|
|
||||||
|
def test_set_config(self):
|
||||||
|
# given
|
||||||
|
node = mock.MagicMock()
|
||||||
|
service = MyService(node)
|
||||||
|
custom_config = {"value1": "1", "value2": "2", "value3": "3"}
|
||||||
|
|
||||||
|
# when
|
||||||
|
service.set_config(custom_config)
|
||||||
|
|
||||||
|
# then
|
||||||
|
assert service.custom_config == custom_config
|
||||||
|
|
||||||
|
def test_set_config_exception(self):
|
||||||
|
# given
|
||||||
|
node = mock.MagicMock()
|
||||||
|
service = MyService(node)
|
||||||
|
custom_config = {"value4": "1"}
|
||||||
|
|
||||||
|
# when
|
||||||
|
with pytest.raises(CoreError):
|
||||||
|
service.set_config(custom_config)
|
||||||
|
|
||||||
|
def test_start_blocking(self):
|
||||||
|
# given
|
||||||
|
node = mock.MagicMock()
|
||||||
|
service = MyService(node)
|
||||||
|
service.create_dirs = mock.MagicMock()
|
||||||
|
service.create_files = mock.MagicMock()
|
||||||
|
service.run_startup = mock.MagicMock()
|
||||||
|
service.run_validation = mock.MagicMock()
|
||||||
|
service.wait_validation = mock.MagicMock()
|
||||||
|
|
||||||
|
# when
|
||||||
|
service.start()
|
||||||
|
|
||||||
|
# then
|
||||||
|
service.create_files.assert_called_once()
|
||||||
|
service.create_dirs.assert_called_once()
|
||||||
|
service.run_startup.assert_called_once()
|
||||||
|
service.run_validation.assert_not_called()
|
||||||
|
service.wait_validation.assert_not_called()
|
||||||
|
|
||||||
|
def test_start_timer(self):
|
||||||
|
# given
|
||||||
|
node = mock.MagicMock()
|
||||||
|
service = MyService(node)
|
||||||
|
service.validation_mode = ConfigServiceMode.TIMER
|
||||||
|
service.create_dirs = mock.MagicMock()
|
||||||
|
service.create_files = mock.MagicMock()
|
||||||
|
service.run_startup = mock.MagicMock()
|
||||||
|
service.run_validation = mock.MagicMock()
|
||||||
|
service.wait_validation = mock.MagicMock()
|
||||||
|
|
||||||
|
# when
|
||||||
|
service.start()
|
||||||
|
|
||||||
|
# then
|
||||||
|
service.create_files.assert_called_once()
|
||||||
|
service.create_dirs.assert_called_once()
|
||||||
|
service.run_startup.assert_called_once()
|
||||||
|
service.run_validation.assert_not_called()
|
||||||
|
service.wait_validation.assert_called_once()
|
||||||
|
|
||||||
|
def test_start_non_blocking(self):
|
||||||
|
# given
|
||||||
|
node = mock.MagicMock()
|
||||||
|
service = MyService(node)
|
||||||
|
service.validation_mode = ConfigServiceMode.NON_BLOCKING
|
||||||
|
service.create_dirs = mock.MagicMock()
|
||||||
|
service.create_files = mock.MagicMock()
|
||||||
|
service.run_startup = mock.MagicMock()
|
||||||
|
service.run_validation = mock.MagicMock()
|
||||||
|
service.wait_validation = mock.MagicMock()
|
||||||
|
|
||||||
|
# when
|
||||||
|
service.start()
|
||||||
|
|
||||||
|
# then
|
||||||
|
service.create_files.assert_called_once()
|
||||||
|
service.create_dirs.assert_called_once()
|
||||||
|
service.run_startup.assert_called_once()
|
||||||
|
service.run_validation.assert_called_once()
|
||||||
|
service.wait_validation.assert_not_called()
|
Loading…
Add table
Reference in a new issue