Files
mesh-catalog/modules/systemd/cmd/systemd-tools/client_test.go
T
jochen 73bb597c16
mesh/merge-gate pass: builds docker, gitea, lab, nftables, slack, systemd → ace, g14, novox, shanks; no bus step; 4 wait(s) for a person; every machine com…
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery delivered
Hold what the tools say to the glossary's retired words (hq ADR 0244)
An agent meets the mesh's words most often in tool descriptions, and
nothing compared them with the glossary: several still said "the host"
for the node-engine and the forge's pull request comment was headed
"Change plan", a word retired twice over. retired-words is the copy of
the words the glossary retires for the tools, and checks/words fails the
repository check when any string a module's code can show, or any
manifest description, uses one. Those found are reworded here.
2026-10-07 20:02:09 +02:00

295 lines
11 KiB
Go

package main
// The service manager's verbs over a fake runner (novox/hq ADR 0177, to-be 41 WP4), ported with the
// TypeScript module's tests: which manager a call reaches and how, acts on the system manager escalated,
// failures named rather than read as empty answers, whether the mesh declares a unit — and the system
// journal read escalated (novox/hq issue 255).
import (
"encoding/json"
"errors"
"os"
"reflect"
"strings"
"testing"
)
type call struct {
cmd string
args []string
env []string
}
func fake(answer func(c call) Ran, calls *[]call) Runner {
return func(cmd string, args []string, env []string) Ran {
c := call{cmd, args, env}
if calls != nil {
*calls = append(*calls, c)
}
return answer(c)
}
}
const (
list = "sshd.service loaded active running OpenSSH Daemon\nbroken.service loaded failed failed A broken thing\n"
showMesh = "LoadState=loaded\nActiveState=active\nSubState=running\nUnitFileState=enabled\nMainPID=42\nExecMainStatus=0\nDescription=showcase, a mesh daemon\nFragmentPath=/etc/systemd/system/showcase.service\n"
showPackage = "LoadState=loaded\nActiveState=active\nSubState=running\nUnitFileState=enabled\nMainPID=7\nExecMainStatus=0\nDescription=OpenSSH Daemon\nFragmentPath=/usr/lib/systemd/system/sshd.service\n"
)
var files = map[string]string{
"/etc/systemd/system/showcase.service": MeshUnitHeader + " Do not edit — this file is replaced whenever the\n[Unit]\n",
"/usr/lib/systemd/system/sshd.service": "[Unit]\nDescription=OpenSSH Daemon\n",
}
func read(p string) (string, error) {
if s, ok := files[p]; ok {
return s, nil
}
return "", errors.New("ENOENT")
}
func manager(run Runner, uid int, name string) *Manager {
return &Manager{Account: "operator", UID: uid, User: name, Run: run, Read: read, Env: []string{"HOME=/h"}}
}
func operator(run Runner) *Manager { return manager(run, 1000, "operator") }
func TestAnActAndASystemJournalReadGoThroughSudoUnlessRoot(t *testing.T) {
for _, verb := range []string{"start", "stop", "restart", "enable", "disable"} {
if p, _ := escalated("systemctl", []string{verb, "x.service"}, System, 1000); p != "sudo" {
t.Errorf("%s was not escalated", verb)
}
}
if p, a := escalated("systemctl", []string{"restart", "sshd.service"}, System, 1000); p != "sudo" || !reflect.DeepEqual(a, []string{"-n", "systemctl", "restart", "sshd.service"}) {
t.Errorf("%s %v", p, a)
}
if p, _ := escalated("systemctl", []string{"restart", "sshd.service"}, System, 0); p != "systemctl" {
t.Error("root escalated")
}
for _, args := range [][]string{{"show", "sshd.service"}, {"list-units", "restart"}} {
if p, _ := escalated("systemctl", args, System, 1000); p != "systemctl" {
t.Errorf("a read was escalated: %v", args)
}
}
if p, _ := escalated("systemctl", []string{"--user", "restart", "x.service"}, User, 1000); p != "systemctl" {
t.Error("the user scope was escalated")
}
// The system journal is read as root: unescalated, an account outside the journal's group sees only
// its own entries and every service's journal reads empty (issue 255).
if p, a := escalated("journalctl", []string{"-u", "x"}, System, 1000); p != "sudo" || a[1] != "journalctl" {
t.Errorf("the system journal read was not escalated: %s %v", p, a)
}
if p, _ := escalated("journalctl", []string{"--user", "-u", "x"}, User, 1000); p != "journalctl" {
t.Error("the account's own journal was escalated")
}
}
func TestTheUserScopeIsPlainUserWithTheAccountsRuntimeDirectoryAndBus(t *testing.T) {
var calls []call
m := manager(fake(func(call) Ran { return Ran{Stdout: list} }, &calls), 1234, "operator")
if _, err := m.Units(User, ""); err != nil {
t.Fatal(err)
}
if calls[0].cmd != "systemctl" || calls[0].args[0] != "--user" {
t.Fatalf("%+v", calls[0])
}
env := strings.Join(calls[0].env, "\n")
if !strings.Contains(env, "XDG_RUNTIME_DIR=/run/user/1234") || !strings.Contains(env, "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1234/bus") || !strings.Contains(env, "HOME=/h") {
t.Fatalf("%v", calls[0].env)
}
if _, err := m.Journal(User, "watcher.service", 10); err != nil {
t.Fatal(err)
}
if calls[1].cmd != "journalctl" || calls[1].args[0] != "--user" {
t.Fatalf("%+v", calls[1])
}
}
func TestTheSystemScopeIsGivenNoSessionWords(t *testing.T) {
var calls []call
if _, err := operator(fake(func(call) Ran { return Ran{Stdout: list} }, &calls)).Units(System, ""); err != nil {
t.Fatal(err)
}
if calls[0].env != nil {
t.Fatalf("%v", calls[0].env)
}
for _, a := range calls[0].args {
if a == "--user" {
t.Fatal("--user in a system call")
}
}
}
func TestTheUserScopeFromAnotherAccountIsRefused(t *testing.T) {
m := manager(fake(func(call) Ran { return Ran{Stdout: list} }, nil), 0, "root")
if _, err := m.Units(User, ""); err == nil || !strings.Contains(err.Error(), "operator's service manager, and this runs as root") {
t.Fatalf("%v", err)
}
}
func TestASystemActEscalatesAndAnswersTheStateAfter(t *testing.T) {
var calls []call
m := operator(fake(func(c call) Ran {
if contains(c.args, "show") {
return Ran{Stdout: showPackage}
}
return Ran{}
}, &calls))
r, err := m.Act(System, "restart", "sshd.service")
if err != nil {
t.Fatal(err)
}
if got := append([]string{calls[0].cmd}, calls[0].args...); !reflect.DeepEqual(got, []string{"sudo", "-n", "systemctl", "restart", "sshd.service"}) {
t.Fatalf("%v", got)
}
if r["ok"] != true || r["active"] != "active" || r["mesh_declared"] != false || r["note"] != nil {
t.Fatalf("%v", r)
}
}
func TestTheRestoreNoteIsOnlyOnAUnitTheMeshDeclares(t *testing.T) {
m := operator(fake(func(c call) Ran {
if contains(c.args, "show") {
return Ran{Stdout: showMesh}
}
return Ran{}
}, nil))
r, _ := m.Act(System, "stop", "showcase.service")
if r["mesh_declared"] != true || !strings.Contains(r["note"].(string), "node-engine restores its declared state") {
t.Fatalf("%v", r)
}
}
func TestStatusSaysWhetherTheMeshDeclaresTheUnit(t *testing.T) {
answer := func(s string) Runner { return fake(func(call) Ran { return Ran{Stdout: s} }, nil) }
mesh, _ := operator(answer(showMesh)).Status(System, "showcase.service")
pkg, _ := operator(answer(showPackage)).Status(System, "sshd.service")
none, _ := operator(answer("LoadState=not-found\nFragmentPath=\n")).Status(System, "nope.service")
if mesh["mesh_declared"] != true || mesh["MainPID"] != "42" || pkg["mesh_declared"] != false || none["mesh_declared"] != false {
t.Fatalf("%v %v %v", mesh, pkg, none)
}
if MeshUnitHeader != "# Generated by the mesh." {
t.Fatal("the header is not the one the host writes")
}
}
func TestRefusalsAreNamed(t *testing.T) {
refused := operator(fake(func(call) Ran { return Ran{Status: 1, Stderr: "sudo: a password is required\n"} }, nil))
if _, err := refused.Act(System, "start", "x.service"); err == nil || !strings.Contains(err.Error(), "may not run it without a prompt: sudo: a password is required") {
t.Fatalf("%v", err)
}
missing := operator(fake(func(call) Ran { return Ran{Status: 127, Error: "ENOENT"} }, nil))
if _, err := missing.Act(System, "start", "x.service"); err == nil || !strings.Contains(err.Error(), "sudo is not installed here") {
t.Fatalf("%v", err)
}
polkitRefused := manager(fake(func(call) Ran {
return Ran{Status: 1, Stderr: "Failed to stop x.service: Access denied as the requested operation requires interactive authentication.\n"}
}, nil), 0, "root")
if _, err := polkitRefused.Act(System, "stop", "x.service"); err == nil || !strings.Contains(err.Error(), "the service manager refused the runtime's account") {
t.Fatalf("%v", err)
}
failing := operator(fake(func(call) Ran { return Ran{Status: 1, Stderr: "Failed to list units: Connection timed out\n"} }, nil))
if _, err := failing.Units(System, ""); err == nil || !strings.Contains(err.Error(), "systemctl failed (1): Failed to list units: Connection timed out") {
t.Fatalf("%v", err)
}
}
func TestAnUnreachableUserManagerIsSaidEvenWhenSystemctlExitsZero(t *testing.T) {
said := "Failed to connect to user scope bus via local transport: No such file or directory\n"
m := operator(fake(func(call) Ran { return Ran{Stderr: said} }, nil))
if _, err := m.Units(User, ""); err == nil || !strings.Contains(err.Error(), "operator's own service manager does not answer at /run/user/1000") {
t.Fatalf("%v", err)
}
nonzero := operator(fake(func(call) Ran { return Ran{Status: 1, Stderr: said} }, nil))
if _, err := nonzero.Status(User, "x.service"); err == nil || !strings.Contains(err.Error(), "does not answer") {
t.Fatalf("%v", err)
}
}
func TestFailedReportsEachManagerAndOneThatDoesNotAnswerByItsError(t *testing.T) {
m := operator(fake(func(c call) Ran {
if c.args[0] == "--user" {
return Ran{Status: 1, Stderr: "Failed to connect to user scope bus via local transport: No such file or directory\n"}
}
return Ran{Stdout: list}
}, nil))
r := m.Failed()
system, _ := json.Marshal(r["system"])
if string(system) != `[{"unit":"broken.service","load":"loaded","active":"failed","sub":"failed","description":"A broken thing"}]` {
t.Fatalf("%s", system)
}
if e, ok := r["user"].(map[string]string); !ok || !strings.Contains(e["error"], "does not answer") {
t.Fatalf("%v", r["user"])
}
}
func TestAUnitsNameIsNeverAnOption(t *testing.T) {
for _, bad := range []string{"--host=elsewhere", "a b", ""} {
if unitArg(bad) == nil {
t.Errorf("%q accepted", bad)
}
}
var calls []call
m := operator(fake(func(call) Ran { return Ran{Stdout: list} }, &calls))
if _, err := m.Act(System, "stop", "-H"); err == nil || len(calls) != 0 {
t.Fatalf("an option ran as a unit: %v %d", err, len(calls))
}
if _, err := m.Units(System, "-x*"); err != nil {
t.Fatal(err)
}
if a := calls[0].args; a[len(a)-2] != "--" || a[len(a)-1] != "-x*" {
t.Fatalf("%v", a)
}
}
// The manifest owns the systemd package, claims the seat's eight verbs, and lists exactly the tools served.
func TestTheManifestOwnsThePackageAndListsWhatIsServed(t *testing.T) {
raw, err := os.ReadFile("../../module.json")
if err != nil {
t.Fatal(err)
}
var m struct {
Capabilities []string `json:"capabilities"`
Tools []string `json:"tools"`
Claims []struct {
Name string `json:"name"`
Serves []string `json:"serves"`
} `json:"claims"`
Resources []struct {
Type string `json:"type"`
Package string `json:"package"`
} `json:"resources"`
}
_ = json.Unmarshal(raw, &m)
owns := false
for _, r := range m.Resources {
owns = owns || (r.Type == "package" && r.Package == "systemd")
}
if !owns || !contains(m.Capabilities, "package-manager") {
t.Fatal("the manifest does not own the systemd package")
}
served := map[string]bool{}
for _, tool := range tools(operator(nil)) {
served[tool.Name] = true
}
want := map[string]bool{}
for _, v := range m.Claims[0].Serves {
want[seat+"."+v] = true
}
for _, n := range m.Tools {
want[n] = true
}
if !reflect.DeepEqual(served, want) {
t.Fatalf("served %v, the manifest says %v", served, want)
}
}
func contains(list []string, s string) bool {
for _, x := range list {
if x == s {
return true
}
}
return false
}