mirror of
https://github.com/QIDITECH/moonraker.git
synced 2026-01-31 00:28:45 +03:00
QIDI moonraker
This commit is contained in:
496
tests/test_config.py
Normal file
496
tests/test_config.py
Normal file
@@ -0,0 +1,496 @@
|
||||
from __future__ import annotations
|
||||
import pathlib
|
||||
import pytest
|
||||
import hashlib
|
||||
import confighelper
|
||||
import shutil
|
||||
import time
|
||||
from confighelper import ConfigError
|
||||
from moonraker import Server
|
||||
from utils import ServerError
|
||||
from components import gpio
|
||||
from mocks import MockGpiod
|
||||
from typing import TYPE_CHECKING, Dict
|
||||
if TYPE_CHECKING:
|
||||
from confighelper import ConfigHelper
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
def config(base_server: Server) -> ConfigHelper:
|
||||
base_server.load_component(base_server.config, "secrets")
|
||||
return base_server.config
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
def test_config(config: ConfigHelper,
|
||||
path_args: Dict[str, pathlib.Path]
|
||||
) -> ConfigHelper:
|
||||
assets = path_args['asset_path']
|
||||
sup_cfg_path = assets.joinpath("moonraker/supplemental.conf")
|
||||
if not sup_cfg_path.exists():
|
||||
pytest.fail("Supplemental config not found")
|
||||
cfg = config.read_supplemental_config(str(sup_cfg_path))
|
||||
return cfg["test_options"]
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def gpio_config(test_config: ConfigHelper,
|
||||
monkeypatch: pytest.MonkeyPatch
|
||||
) -> ConfigHelper:
|
||||
def load_gpio_mock(name: str) -> MockGpiod:
|
||||
return MockGpiod()
|
||||
monkeypatch.setattr(gpio, "load_system_module", load_gpio_mock)
|
||||
yield test_config
|
||||
server = test_config.get_server()
|
||||
gpio_comp = server.lookup_component("gpio", None)
|
||||
if gpio_comp is not None:
|
||||
gpio_comp.close()
|
||||
gpio_comp.reserved_gpios = {}
|
||||
|
||||
class TestConfigGeneric:
|
||||
def test_get_server(self, config: ConfigHelper):
|
||||
server = config.get_server()
|
||||
assert isinstance(server, Server)
|
||||
|
||||
def test_get_item(self, config: ConfigHelper):
|
||||
sec = config["file_manager"]
|
||||
assert sec.section == "file_manager"
|
||||
|
||||
def test_no_section_fail(self, config: ConfigHelper):
|
||||
with pytest.raises(ConfigError):
|
||||
config["not_available"].get("no_section")
|
||||
|
||||
def test_contains(self, config: ConfigHelper):
|
||||
assert "file_manager" in config
|
||||
|
||||
def test_not_contains(self, config: ConfigHelper):
|
||||
assert "not_available" not in config
|
||||
|
||||
def test_has_option(self, config: ConfigHelper):
|
||||
assert config.has_option("host")
|
||||
|
||||
def test_get_name(self, config: ConfigHelper):
|
||||
assert config.get_name() == "server"
|
||||
|
||||
def test_get_options(self,
|
||||
config: ConfigHelper,
|
||||
path_args: Dict[str, pathlib.Path]):
|
||||
expected = {
|
||||
"host": "0.0.0.0",
|
||||
"port": "7010",
|
||||
"ssl_port": "7011",
|
||||
"klippy_uds_address": str(path_args["klippy_uds_path"])
|
||||
}
|
||||
assert expected == config.get_options()
|
||||
|
||||
def test_get_hash(self, config: ConfigHelper):
|
||||
opts = config.get_options()
|
||||
expected_hash = hashlib.sha256()
|
||||
for opt, val in opts.items():
|
||||
expected_hash.update(opt.encode())
|
||||
expected_hash.update(val.encode())
|
||||
cfg_hash = config.get_hash().hexdigest()
|
||||
assert cfg_hash == expected_hash.hexdigest()
|
||||
|
||||
def test_missing_supplemental_config(config: ConfigHelper):
|
||||
no_file = pathlib.Path("nofile")
|
||||
with pytest.raises(ConfigError):
|
||||
config.read_supplemental_config(no_file)
|
||||
|
||||
def test_error_supplemental_config(config: ConfigHelper,
|
||||
path_args: Dict[str, pathlib.Path]):
|
||||
assets = path_args["asset_path"]
|
||||
invalid_cfg = assets.joinpath("moonraker/invalid_config.conf")
|
||||
if not invalid_cfg.exists():
|
||||
pytest.fail("Invalid Config File does not exist")
|
||||
with pytest.raises(ConfigError):
|
||||
config.read_supplemental_config(invalid_cfg)
|
||||
|
||||
def test_prefix_sections(test_config: ConfigHelper):
|
||||
prefix = test_config.get_prefix_sections("prefix_sec")
|
||||
expected = ["prefix_sec one", "prefix_sec two", "prefix_sec three"]
|
||||
assert prefix == expected
|
||||
|
||||
class TestGetString:
|
||||
def test_get_str_exists(self, test_config: ConfigHelper):
|
||||
val = test_config.get("test_string")
|
||||
assert val == "Hello World"
|
||||
|
||||
def test_get_str_fail(self, test_config: ConfigHelper):
|
||||
with pytest.raises(ConfigError):
|
||||
test_config.get("invalid_option")
|
||||
|
||||
def test_get_str_default(self, test_config: ConfigHelper):
|
||||
assert test_config.get("invalid_option", None) is None
|
||||
|
||||
def test_get_str_deprecate(self, test_config: ConfigHelper):
|
||||
server = test_config.get_server()
|
||||
test_config.get("test_string", deprecate=True)
|
||||
expected = (
|
||||
f"[test_options]: Option 'test_string' is "
|
||||
"deprecated, see the configuration documention "
|
||||
"at https://moonraker.readthedocs.io/en/latest/configuration"
|
||||
)
|
||||
assert expected in server.warnings
|
||||
|
||||
class TestGetInt:
|
||||
def test_get_int_exists(self, test_config: ConfigHelper):
|
||||
val = test_config.getint("test_int")
|
||||
assert val == 1
|
||||
|
||||
def test_get_int_fail(self, test_config: ConfigHelper):
|
||||
with pytest.raises(ConfigError):
|
||||
test_config.getint("invalid_option")
|
||||
|
||||
def test_get_int_default(self, test_config: ConfigHelper):
|
||||
assert test_config.getint("invalid_option", None) is None
|
||||
|
||||
def test_get_int_fail_above(self, test_config: ConfigHelper):
|
||||
with pytest.raises(ConfigError):
|
||||
test_config.getint("test_int", above=1)
|
||||
|
||||
def test_get_int_fail_below(self, test_config: ConfigHelper):
|
||||
with pytest.raises(ConfigError):
|
||||
test_config.getint("test_int", below=1)
|
||||
|
||||
def test_get_int_fail_minval(self, test_config: ConfigHelper):
|
||||
with pytest.raises(ConfigError):
|
||||
test_config.getint("test_int", minval=2)
|
||||
|
||||
def test_get_int_fail_maxval(self, test_config: ConfigHelper):
|
||||
with pytest.raises(ConfigError):
|
||||
test_config.getint("test_int", maxval=0)
|
||||
|
||||
def test_get_int_pass_all(self, test_config: ConfigHelper):
|
||||
val = test_config.getint("test_int", above=0, below=2,
|
||||
minval=1, maxval=1)
|
||||
assert val == 1
|
||||
|
||||
def test_get_int_deprecate(self, test_config: ConfigHelper):
|
||||
server = test_config.get_server()
|
||||
test_config.getint("test_int", deprecate=True)
|
||||
expected = (
|
||||
f"[test_options]: Option 'test_int' is "
|
||||
"deprecated, see the configuration documention "
|
||||
"at https://moonraker.readthedocs.io/en/latest/configuration"
|
||||
)
|
||||
assert expected in server.warnings
|
||||
|
||||
class TestGetFloat:
|
||||
def test_get_float_exists(self, test_config: ConfigHelper):
|
||||
val = test_config.getfloat("test_float")
|
||||
assert 3.5 == pytest.approx(val)
|
||||
|
||||
def test_get_float_fail(self, test_config: ConfigHelper):
|
||||
with pytest.raises(ConfigError):
|
||||
test_config.getfloat("invalid_option")
|
||||
|
||||
def test_get_float_default(self, test_config: ConfigHelper):
|
||||
assert test_config.getfloat("invalid_option", None) is None
|
||||
|
||||
def test_get_float_fail_above(self, test_config: ConfigHelper):
|
||||
with pytest.raises(ConfigError):
|
||||
test_config.getfloat("test_float", above=3.55)
|
||||
|
||||
def test_get_float_fail_below(self, test_config: ConfigHelper):
|
||||
with pytest.raises(ConfigError):
|
||||
test_config.getfloat("test_float", below=3.45)
|
||||
|
||||
def test_get_float_fail_minval(self, test_config: ConfigHelper):
|
||||
with pytest.raises(ConfigError):
|
||||
test_config.getfloat("test_float", minval=3.6)
|
||||
|
||||
def test_get_float_fail_maxval(self, test_config: ConfigHelper):
|
||||
with pytest.raises(ConfigError):
|
||||
test_config.getfloat("test_float", maxval=3.45)
|
||||
|
||||
def test_get_float_pass_all(self, test_config: ConfigHelper):
|
||||
val = test_config.getfloat("test_float", above=3.45, below=3.55,
|
||||
minval=3, maxval=4)
|
||||
assert 3.5 == pytest.approx(val)
|
||||
|
||||
def test_get_float_deprecate(self, test_config: ConfigHelper):
|
||||
server = test_config.get_server()
|
||||
test_config.getfloat("test_float", deprecate=True)
|
||||
expected = (
|
||||
f"[test_options]: Option 'test_float' is "
|
||||
"deprecated, see the configuration documention "
|
||||
"at https://moonraker.readthedocs.io/en/latest/configuration"
|
||||
)
|
||||
assert expected in server.warnings
|
||||
|
||||
class TestGetBoolean:
|
||||
def test_get_boolean_exists(self, test_config: ConfigHelper):
|
||||
val = test_config.getboolean("test_bool")
|
||||
assert val is True
|
||||
|
||||
def test_get_float_fail(self, test_config: ConfigHelper):
|
||||
with pytest.raises(ConfigError):
|
||||
test_config.getboolean("invalid_option")
|
||||
|
||||
def test_get_float_default(self, test_config: ConfigHelper):
|
||||
assert test_config.getboolean("invalid_option", None) is None
|
||||
|
||||
def test_get_int_deprecate(self, test_config: ConfigHelper):
|
||||
server = test_config.get_server()
|
||||
test_config.getboolean("test_bool", deprecate=True)
|
||||
expected = (
|
||||
f"[test_options]: Option 'test_bool' is "
|
||||
"deprecated, see the configuration documention "
|
||||
"at https://moonraker.readthedocs.io/en/latest/configuration"
|
||||
)
|
||||
assert expected in server.warnings
|
||||
|
||||
class TestGetList:
|
||||
def test_get_list_exists(self, test_config: ConfigHelper):
|
||||
val = test_config.getlist("test_list")
|
||||
assert val == ["one", "two", "three"]
|
||||
|
||||
def test_get_list_fail(self, test_config: ConfigHelper):
|
||||
with pytest.raises(ConfigError):
|
||||
test_config.getlist("invalid_option")
|
||||
|
||||
def test_get_list_default(self, test_config: ConfigHelper):
|
||||
assert test_config.getlist("invalid_option", None) is None
|
||||
|
||||
def test_get_int_list(self, test_config: ConfigHelper):
|
||||
val = test_config.getintlist("test_int_list", separator=",")
|
||||
assert val == [1, 2, 3]
|
||||
|
||||
def test_get_float_list(self, test_config: ConfigHelper):
|
||||
val = test_config.getfloatlist("test_float_list", separator=",")
|
||||
assert val == pytest.approx([1.5, 2.8, 3.2])
|
||||
|
||||
def test_get_multi_list(self, test_config: ConfigHelper):
|
||||
val = test_config.getlists("test_multi_list", list_type=int,
|
||||
separators=("\n", ","))
|
||||
assert val == [[1, 2, 3], [4, 5, 6]]
|
||||
|
||||
def test_get_list_deprecate(self, test_config: ConfigHelper):
|
||||
server = test_config.get_server()
|
||||
test_config.getlist("test_list", deprecate=True)
|
||||
expected = (
|
||||
f"[test_options]: Option 'test_list' is "
|
||||
"deprecated, see the configuration documention "
|
||||
"at https://moonraker.readthedocs.io/en/latest/configuration"
|
||||
)
|
||||
assert expected in server.warnings
|
||||
|
||||
class TestGetDict:
|
||||
def test_get_dict_exists(self, test_config: ConfigHelper):
|
||||
val = test_config.getdict("test_dict", dict_type=int)
|
||||
assert val == {"one": 1, "two": 2, "three": 3}
|
||||
|
||||
def test_get_dict_fail(self, test_config: ConfigHelper):
|
||||
with pytest.raises(ConfigError):
|
||||
test_config.getdict("invalid_option")
|
||||
|
||||
def test_get_dict_default(self, test_config: ConfigHelper):
|
||||
assert test_config.getdict("invalid_option", None) is None
|
||||
|
||||
def test_get_dict_empty_fields(self, test_config: ConfigHelper):
|
||||
val = test_config.getdict("test_dict_empty_field",
|
||||
allow_empty_fields=True)
|
||||
assert val == {"one": "test", "two": None, "three": None}
|
||||
|
||||
def test_get_dict_empty_fields_fail(self, test_config: ConfigHelper):
|
||||
with pytest.raises(ConfigError):
|
||||
test_config.getdict("test_dict_empty_field")
|
||||
|
||||
def test_get_dict_deprecate(self, test_config: ConfigHelper):
|
||||
server = test_config.get_server()
|
||||
test_config.getdict("test_dict", deprecate=True)
|
||||
expected = (
|
||||
f"[test_options]: Option 'test_dict' is "
|
||||
"deprecated, see the configuration documention "
|
||||
"at https://moonraker.readthedocs.io/en/latest/configuration"
|
||||
)
|
||||
assert expected in server.warnings
|
||||
|
||||
class TestGetTemplate:
|
||||
def test_get_template_exists(self, test_config: ConfigHelper):
|
||||
val = test_config.gettemplate("test_template").render()
|
||||
assert val == "mqttuser"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_template_async(self, test_config: ConfigHelper):
|
||||
templ = test_config.gettemplate("test_template", is_async=True)
|
||||
val = await templ.render_async()
|
||||
assert val == "mqttuser"
|
||||
|
||||
def test_get_template_plain(self, test_config: ConfigHelper):
|
||||
val = test_config.gettemplate("test_string").render()
|
||||
assert val == "Hello World"
|
||||
|
||||
def test_get_template_fail(self, test_config: ConfigHelper):
|
||||
with pytest.raises(ConfigError):
|
||||
test_config.gettemplate("invalid_option")
|
||||
|
||||
def test_get_template_render_fail(self, test_config: ConfigHelper):
|
||||
with pytest.raises(ServerError):
|
||||
test_config.gettemplate("test_template", is_async=True).render()
|
||||
|
||||
def test_get_template_default(self, test_config: ConfigHelper):
|
||||
assert test_config.gettemplate("invalid_option", None) is None
|
||||
|
||||
def test_load_template(self, test_config: ConfigHelper):
|
||||
val = test_config.load_template("test_template").render()
|
||||
assert val == "mqttuser"
|
||||
|
||||
def test_load_template_default(self, test_config: ConfigHelper):
|
||||
templ = test_config.load_template(
|
||||
"invalid_option", "{secrets.mqtt_credentials.password}")
|
||||
val = templ.render()
|
||||
assert val == "mqttpass"
|
||||
|
||||
def test_get_template_deprecate(self, test_config: ConfigHelper):
|
||||
server = test_config.get_server()
|
||||
test_config.gettemplate("test_template", deprecate=True)
|
||||
expected = (
|
||||
f"[test_options]: Option 'test_template' is "
|
||||
"deprecated, see the configuration documention "
|
||||
"at https://moonraker.readthedocs.io/en/latest/configuration"
|
||||
)
|
||||
assert expected in server.warnings
|
||||
|
||||
class TestGetGpioOut:
|
||||
def test_get_gpio_exists(self, gpio_config: ConfigHelper):
|
||||
val: gpio.GpioOutputPin = gpio_config.getgpioout("test_gpio")
|
||||
assert (
|
||||
val.orig == "gpiochip0/gpio26" and
|
||||
val.name == "gpiochip0:gpio26" and
|
||||
val.inverted is False and
|
||||
val.value == 0
|
||||
)
|
||||
|
||||
def test_get_gpio_no_chip(self, gpio_config: ConfigHelper):
|
||||
val: gpio.GpioOutputPin = gpio_config.getgpioout("test_gpio_no_chip")
|
||||
assert (
|
||||
val.orig == "gpio26" and
|
||||
val.name == "gpiochip0:gpio26" and
|
||||
val.inverted is False and
|
||||
val.value == 0
|
||||
)
|
||||
|
||||
def test_get_gpio_invert(self, gpio_config: ConfigHelper):
|
||||
val: gpio.GpioOutputPin = gpio_config.getgpioout("test_gpio_invert")
|
||||
assert (
|
||||
val.orig == "!gpiochip0/gpio26" and
|
||||
val.name == "gpiochip0:gpio26" and
|
||||
val.inverted is True and
|
||||
val.value == 0
|
||||
)
|
||||
|
||||
def test_get_gpio_no_chip_invert(self, gpio_config: ConfigHelper):
|
||||
val: gpio.GpioOutputPin = gpio_config.getgpioout(
|
||||
"test_gpio_no_chip_invert")
|
||||
assert (
|
||||
val.orig == "!gpio26" and
|
||||
val.name == "gpiochip0:gpio26" and
|
||||
val.inverted is True and
|
||||
val.value == 0
|
||||
)
|
||||
|
||||
def test_get_gpio_initial_value(self, gpio_config: ConfigHelper):
|
||||
val: gpio.GpioOutputPin = gpio_config.getgpioout(
|
||||
"test_gpio", initial_value=1)
|
||||
assert (
|
||||
val.orig == "gpiochip0/gpio26" and
|
||||
val.name == "gpiochip0:gpio26" and
|
||||
val.inverted is False and
|
||||
val.value == 1
|
||||
)
|
||||
|
||||
def test_get_gpio_fail(self, gpio_config: ConfigHelper):
|
||||
with pytest.raises(ConfigError):
|
||||
gpio_config.getgpioout("invalid_option")
|
||||
|
||||
def test_get_gpio_default(self, gpio_config: ConfigHelper):
|
||||
assert gpio_config.getgpioout("invalid_option", None) is None
|
||||
|
||||
@pytest.mark.parametrize("opt", ["pullup", "pullup_no_chip",
|
||||
"pulldown", "pulldown_no_chip"])
|
||||
def test_get_gpio_invalid(self, gpio_config: ConfigHelper, opt: str):
|
||||
option = f"test_gpio_{opt}"
|
||||
if not gpio_config.has_option(option):
|
||||
pytest.fail(f"No option {option}")
|
||||
with pytest.raises(ConfigError):
|
||||
gpio_config.getgpioout(option)
|
||||
|
||||
def test_get_gpio_deprecated(self, gpio_config: ConfigHelper):
|
||||
server = gpio_config.get_server()
|
||||
gpio_config.getgpioout("test_gpio", deprecate=True)
|
||||
expected = (
|
||||
f"[test_options]: Option 'test_gpio' is "
|
||||
"deprecated, see the configuration documention "
|
||||
"at https://moonraker.readthedocs.io/en/latest/configuration"
|
||||
)
|
||||
assert expected in server.warnings
|
||||
|
||||
class TestGetConfiguration:
|
||||
def test_get_config_no_exist(self, base_server: Server):
|
||||
fake_path = pathlib.Path("no_exist")
|
||||
if fake_path.exists():
|
||||
pytest.fail("Path exists")
|
||||
args = dict(base_server.app_args)
|
||||
args["config_file"] = str(fake_path)
|
||||
with pytest.raises(ConfigError):
|
||||
confighelper.get_configuration(base_server, args)
|
||||
|
||||
def test_get_config_no_access(self,
|
||||
base_server: Server,
|
||||
path_args: Dict[str, pathlib.Path]
|
||||
):
|
||||
cfg_path = path_args["config_path"]
|
||||
test_cfg = cfg_path.joinpath("test.conf")
|
||||
shutil.copy(path_args["moonraker.conf"], test_cfg)
|
||||
test_cfg.chmod(mode=222)
|
||||
args = dict(base_server.app_args)
|
||||
args["config_file"] = str(test_cfg)
|
||||
with pytest.raises(ConfigError):
|
||||
confighelper.get_configuration(base_server, args)
|
||||
|
||||
def test_get_config_no_server(self,
|
||||
base_server: Server,
|
||||
path_args: Dict[str, pathlib.Path]
|
||||
):
|
||||
assets = path_args['asset_path']
|
||||
sup_cfg_path = assets.joinpath("moonraker/supplemental.conf")
|
||||
if not sup_cfg_path.exists():
|
||||
pytest.fail("Supplemental config not found")
|
||||
args = dict(base_server.app_args)
|
||||
args["config_file"] = str(sup_cfg_path)
|
||||
with pytest.raises(ConfigError):
|
||||
confighelper.get_configuration(base_server, args)
|
||||
|
||||
class TestBackupConfig:
|
||||
def test_find_backup_fail(self):
|
||||
fake_path = pathlib.Path("no_exist")
|
||||
if fake_path.exists():
|
||||
fake_path.unlink()
|
||||
result = confighelper.find_config_backup(fake_path)
|
||||
assert result is None
|
||||
|
||||
def test_backup_config_success(
|
||||
self, path_args: Dict[str, pathlib.Path], config: ConfigHelper
|
||||
):
|
||||
cfg_path = path_args["moonraker.conf"]
|
||||
bkp_dest = cfg_path.parent.joinpath(f".{cfg_path.name}.bkp")
|
||||
if bkp_dest.exists():
|
||||
pytest.fail("Backup Already Exists")
|
||||
config.create_backup()
|
||||
assert bkp_dest.is_file()
|
||||
|
||||
def test_backup_skip(
|
||||
self, path_args: Dict[str, pathlib.Path], config: ConfigHelper
|
||||
):
|
||||
cfg_path = path_args["moonraker.conf"]
|
||||
bkp_dest = cfg_path.parent.joinpath(f".{cfg_path.name}.bkp")
|
||||
if not bkp_dest.exists():
|
||||
pytest.fail("Backup Not Present")
|
||||
stat = bkp_dest.stat()
|
||||
config.create_backup()
|
||||
assert stat == bkp_dest.stat()
|
||||
|
||||
def test_find_backup(self, path_args: Dict[str, pathlib.Path]):
|
||||
cfg_path = path_args["moonraker.conf"]
|
||||
bkp_dest = cfg_path.parent.joinpath(f".{cfg_path.name}.bkp")
|
||||
bkp = confighelper.find_config_backup(str(cfg_path))
|
||||
assert bkp == str(bkp_dest)
|
||||
Reference in New Issue
Block a user