Files
pyMC_Repeater/tests/test_service_utils.py
T

225 lines
6.9 KiB
Python

import io
import subprocess
from unittest.mock import MagicMock
import pytest
from repeater import service_utils as su
def test_is_buildroot_via_metadata_file(monkeypatch):
monkeypatch.setattr(su.os.path, "exists", lambda p: p == su.BUILDROOT_METADATA_PATH)
assert su.is_buildroot() is True
def test_is_buildroot_via_os_release(monkeypatch):
monkeypatch.setattr(
su.os.path,
"exists",
lambda p: p == "/etc/os-release",
)
monkeypatch.setattr(
"builtins.open",
lambda *args, **kwargs: io.StringIO("NAME=x\nID=buildroot\n"),
)
assert su.is_buildroot() is True
def test_get_buildroot_image_info_parse_and_error(monkeypatch):
monkeypatch.setattr(
"builtins.open",
lambda *args, **kwargs: io.StringIO("\nfoo=bar\ninvalid\nimage_version=1.2.3\n"),
)
info = su.get_buildroot_image_info()
assert info["foo"] == "bar"
assert info["image_version"] == "1.2.3"
assert su.get_buildroot_image_version() == "1.2.3"
def _raise(*_args, **_kwargs):
raise OSError("nope")
monkeypatch.setattr("builtins.open", _raise)
assert su.get_buildroot_image_info() == {}
def test_is_container_detection_paths(monkeypatch):
# /.dockerenv path
monkeypatch.setattr(su.os.path, "exists", lambda p: p == "/.dockerenv")
monkeypatch.delenv("container", raising=False)
assert su.is_container() is True
# env var path
monkeypatch.setattr(su.os.path, "exists", lambda _p: False)
monkeypatch.setenv("container", "docker")
assert su.is_container() is True
@pytest.mark.parametrize(
"environ_bytes,cgroup_text,host_path,expected",
[
(b"abc\x00container=docker\x00", "", False, True),
(b"abc", "1:name=systemd:/docker/abc", False, True),
(b"abc", "1:name=systemd:/", True, True),
(b"abc", "1:name=systemd:/", False, False),
],
)
def test_is_container_proc_and_host_paths(monkeypatch, environ_bytes, cgroup_text, host_path, expected):
monkeypatch.setattr(su.os.path, "exists", lambda p: p == "/run/host/container-manager" and host_path)
monkeypatch.delenv("container", raising=False)
def _open(path, mode="r", encoding=None):
if path == "/proc/1/environ":
return io.BytesIO(environ_bytes)
if path == "/proc/1/cgroup":
return io.StringIO(cgroup_text)
raise OSError("unexpected")
monkeypatch.setattr("builtins.open", _open)
assert su.is_container() is expected
def test_get_container_restart_message():
msg = su.get_container_restart_message()
assert "Container restart initiated" in msg
assert "Docker or Home Assistant" in msg
def test_restart_service_container_path(monkeypatch):
monkeypatch.setattr(su, "is_container", lambda: True)
sched = MagicMock()
monkeypatch.setattr(su, "_schedule_container_exit", sched)
ok, msg = su.restart_service()
assert ok is True
assert "Container restart initiated" in msg
sched.assert_called_once()
def test_restart_service_buildroot_paths(monkeypatch):
monkeypatch.setattr(su, "is_container", lambda: False)
monkeypatch.setattr(su, "is_buildroot", lambda: True)
# missing init script
monkeypatch.setattr(su.os.path, "exists", lambda _p: False)
ok, msg = su.restart_service()
assert ok is False
assert "init script not found" in msg
# popen success
monkeypatch.setattr(su.os.path, "exists", lambda p: p == su.INIT_SCRIPT)
monkeypatch.setattr(su.subprocess, "Popen", MagicMock())
ok, msg = su.restart_service()
assert ok is True
assert "Service restart initiated" in msg
# popen failure
def _raise(*_args, **_kwargs):
raise RuntimeError("boom")
monkeypatch.setattr(su.subprocess, "Popen", _raise)
ok, msg = su.restart_service()
assert ok is False
assert "Restart failed" in msg
def test_restart_service_systemctl_and_sudo_paths(monkeypatch):
monkeypatch.setattr(su, "is_container", lambda: False)
monkeypatch.setattr(su, "is_buildroot", lambda: False)
def _result(code=0, err=""):
return subprocess.CompletedProcess(args=[], returncode=code, stdout="", stderr=err)
# systemctl success
monkeypatch.setattr(su.subprocess, "run", lambda *args, **kwargs: _result(0))
ok, msg = su.restart_service()
assert ok is True
assert "Service restart initiated" in msg
# systemctl timeout
def _timeout(*_args, **_kwargs):
raise subprocess.TimeoutExpired(cmd="x", timeout=5)
monkeypatch.setattr(su.subprocess, "run", _timeout)
ok, msg = su.restart_service()
assert ok is True
assert "timeout" in msg
# systemctl missing binary
def _missing(*_args, **_kwargs):
raise FileNotFoundError("systemctl")
monkeypatch.setattr(su.subprocess, "run", _missing)
ok, msg = su.restart_service()
assert ok is False
assert "systemctl not available" in msg
# systemctl denied then sudo success
calls = {"n": 0}
def _denied_then_sudo(*args, **kwargs):
calls["n"] += 1
if calls["n"] == 1:
return _result(1, "Access denied")
return _result(0)
monkeypatch.setattr(su.subprocess, "run", _denied_then_sudo)
ok, msg = su.restart_service()
assert ok is True
assert "Service restart initiated" in msg
# systemctl generic fail then sudo fail
calls = {"n": 0}
def _fail_then_fail(*args, **kwargs):
calls["n"] += 1
if calls["n"] == 1:
return _result(1, "broken")
return _result(2, "sudo denied")
monkeypatch.setattr(su.subprocess, "run", _fail_then_fail)
ok, msg = su.restart_service()
assert ok is False
assert "Restart failed" in msg
# systemctl generic fail then sudo timeout
calls = {"n": 0}
def _fail_then_timeout(*args, **kwargs):
calls["n"] += 1
if calls["n"] == 1:
return _result(1, "broken")
raise subprocess.TimeoutExpired(cmd="sudo", timeout=5)
monkeypatch.setattr(su.subprocess, "run", _fail_then_timeout)
ok, msg = su.restart_service()
assert ok is True
assert "timeout" in msg
# systemctl generic fail then sudo missing
calls = {"n": 0}
def _fail_then_sudo_missing(*args, **kwargs):
calls["n"] += 1
if calls["n"] == 1:
return _result(1, "broken")
raise FileNotFoundError("sudo")
monkeypatch.setattr(su.subprocess, "run", _fail_then_sudo_missing)
ok, msg = su.restart_service()
assert ok is False
assert "Neither polkit nor sudo" in msg
# systemctl generic fail then sudo unexpected exception
calls = {"n": 0}
def _fail_then_exception(*args, **kwargs):
calls["n"] += 1
if calls["n"] == 1:
return _result(1, "broken")
raise RuntimeError("bad")
monkeypatch.setattr(su.subprocess, "run", _fail_then_exception)
ok, msg = su.restart_service()
assert ok is False
assert "Restart command failed" in msg