aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/tests/test_packaging_units.py
blob: bbfb5365a614e6b24bf4fd331a60f78ed7cf548c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
"""
The systemd units, and which directory each belongs in.

`meshbay-node.spec` installed the SYSTEM template — the one carrying `User=%i` —
into the *user* unit directory. A user unit already runs as its owner and cannot
carry `User=`; systemd refuses the file, so the packaged unit could never have
started. Nothing caught it because nothing had built and installed the RPM.

These read the files rather than installing them: no rpmbuild here. Weak
evidence, and enough for this defect, which is a file in the wrong place.
"""

from pathlib import Path

import pytest

ROOT = Path(__file__).resolve().parents[3]
SYSTEMD = ROOT / "packaging" / "systemd"
SPEC = ROOT / "packaging" / "rpm" / "meshbay-node.spec"

pytestmark = pytest.mark.skipif(not SPEC.exists(), reason="packaging not present")


def _system_unit() -> str:
    return (SYSTEMD / "meshbay-node.service").read_text(encoding="utf-8")


def _user_unit() -> str:
    return (SYSTEMD / "meshbay-node-user.service").read_text(encoding="utf-8")


def _directives(unit: str) -> list[str]:
    """
    The lines systemd acts on — comments dropped.

    Searching the whole file finds the comment explaining why a directive is
    absent, and calls that the directive. The same mistake as reading a CSP out
    of the HTML comment above the meta tag.
    """
    return [line.strip() for line in unit.splitlines()
            if line.strip() and not line.strip().startswith("#")]


def test_the_system_unit_is_a_template_that_names_its_user():
    directives = _directives(_system_unit())
    assert any(d == "User=%i" for d in directives), (
        "the system template must run as the instance name")
    assert any("%h" in d for d in directives), "it reads the instance's own home"


def test_the_user_unit_names_no_user():
    """
    It already runs as its owner. `User=` in a user unit is not ignored —
    systemd refuses to load the file at all.
    """
    directives = _directives(_user_unit())
    assert not any(d.startswith("User=") for d in directives)
    assert not any(d.startswith("Group=") for d in directives)


def test_each_unit_is_installed_where_it_can_run():
    spec = SPEC.read_text(encoding="utf-8")
    install = spec.split("%files")[0]

    # The template goes to the system directory, instantiated per person.
    assert "%{_unitdir}/meshbay-node@.service" in install
    # The user unit goes to the user directory, enabled without a password.
    assert "%{_userunitdir}/meshbay-node.service" in install

    system_line = next(l for l in install.splitlines()
                       if "meshbay-node-user.service" in l)
    idx = install.splitlines().index(system_line)
    destination = install.splitlines()[idx + 1]
    assert "_userunitdir" in destination, (
        "the per-user unit is installed as a system unit")


def test_both_units_are_listed_in_files():
    files = SPEC.read_text(encoding="utf-8").split("%files")[1]
    assert "%{_unitdir}/meshbay-node@.service" in files
    assert "%{_userunitdir}/meshbay-node.service" in files


def test_the_user_unit_can_be_reloaded_without_dropping_anyone():
    """
    `meshbay-node reload` sends SIGHUP so a group's directories can change
    without restarting. Without ExecReload the desktop client's reload would
    have to stop the service, which drops whoever is watching a film.
    """
    directives = _directives(_user_unit())
    reload_line = next((d for d in directives if d.startswith("ExecReload=")), "")
    assert reload_line, "no ExecReload"
    assert "HUP" in reload_line


def test_the_user_unit_documents_how_a_drive_outside_home_is_added():
    """
    ProtectSystem=strict hides it, and a volume mounted after the service
    started is invisible inside the unit's mount namespace — so the drop-in
    needs RequiresMountsFor as well as ReadWritePaths. Written down where
    somebody debugging an empty directory will find it.
    """
    unit = _user_unit()
    assert "ProtectSystem=strict" in _directives(unit)
    # These two belong in the comment: they are what an operator has to write in
    # a drop-in, not what this file declares.
    assert "RequiresMountsFor" in unit
    assert "meshbay-node.service.d" in unit