diff options
Diffstat (limited to 'packages/meshbay-node')
| -rw-r--r-- | packages/meshbay-node/tests/test_packaging_units.py | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/packages/meshbay-node/tests/test_packaging_units.py b/packages/meshbay-node/tests/test_packaging_units.py new file mode 100644 index 0000000..bbfb536 --- /dev/null +++ b/packages/meshbay-node/tests/test_packaging_units.py @@ -0,0 +1,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 |