Files
mesh-catalog/modules/pacman/cmd/pacman-tools/manifest_test.go
T
jschoubben eb717cc514 Declare the pacman keeper and its bus shapes, so upgrades run as rounds on the bus instead of a weekly timer on every node at once (issue 1841/S1, with 1840/S5's manifest)
The keeper process runs under its own account with a bus account of its own,
keeps the six states and emits the nine events of ADR 0507 and ADR 0508; its
config (upgrade-order, record-days, round-start-minutes) lands in its config
file and restarts it. shapes.go fixes every key's value and event body as the
interface the keeper and the verbs share. The weekly timer and its service are
dropped, which the node-engine removes.
2026-10-22 14:23:22 +02:00

104 lines
4.0 KiB
Go

package main
// The module's shape (novox/hq to-be 42 Phase 1, ADR 0207, research 027): it holds the
// node-package-manager seat and declares the package manager's own package; it owns pacman.conf
// whole — proven by pacman-conf on the rendered file, because a pacman.conf pacman cannot read is a
// machine that can neither install nor upgrade — and reflector's configuration, with the refresher
// and the cache cleaner on their timers.
import (
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
)
func TestItHoldsThePackageManagerSeatAndDeclaresItsPackage(t *testing.T) {
m := manifest(t)
if len(m.Claims) != 1 || m.Claims[0]["name"] != "node-package-manager" || m.Claims[0]["scope"] != "node" || m.Claims[0]["serves"] != nil {
t.Fatalf("claims: %v", m.Claims)
}
for id, pkg := range map[string]string{"package": "pacman", "contrib": "pacman-contrib", "reflector": "reflector"} {
if r := m.resource(t, id); r["package"] != pkg || r["absent"] != nil {
t.Errorf("%s: %v", id, r)
}
}
for id, unit := range map[string]string{"mirror-refresh": "reflector.timer", "cache-cleaning": "paccache.timer"} {
r := m.resource(t, id)
if r["unit"] != unit || r["state"] != "running" || r["boot"] != "enabled" {
t.Errorf("%s: %v", id, r)
}
}
}
func TestPacmanConfIsWholeTheUnionOfRepositoriesAndTheImprovedOptions(t *testing.T) {
f := manifest(t).resource(t, "config")
content := templateText(t, f)
if f["path"] != "/etc/pacman.conf" || f["into"] != nil || !strings.HasPrefix(content, MeshHeader) {
t.Fatalf("%v", f)
}
var sections []string
for _, l := range strings.Split(content, "\n") {
if strings.HasPrefix(l, "[") {
sections = append(sections, l)
}
}
if strings.Join(sections, " ") != "[options] [core] [extra] [multilib]" {
t.Fatalf("sections: %v", sections)
}
for _, want := range []string{"\nColor\n", "\nCheckSpace\n", "\nVerbosePkgLists\n", "\nParallelDownloads = 5\n", "\nDownloadUser = alpm\n", "\nSigLevel = Required DatabaseOptional\n"} {
if !strings.Contains(content, want) {
t.Errorf("missing %q", strings.TrimSpace(want))
}
}
conf, err := exec.LookPath("pacman-conf")
if err != nil {
t.Skip("pacman-conf is not installed here; the rendered file is not proven")
}
file := filepath.Join(t.TempDir(), "pacman.conf")
if err := os.WriteFile(file, []byte(content), 0o644); err != nil {
t.Fatal(err)
}
repos, err := exec.Command(conf, "--config", file, "--repo-list").CombinedOutput()
if err != nil || strings.Join(strings.Fields(string(repos)), " ") != "core extra multilib" {
t.Fatalf("pacman-conf --repo-list: %v\n%s", err, repos)
}
out, err := exec.Command(conf, "--config", file).CombinedOutput()
if err != nil {
t.Fatalf("pacman-conf refuses the file: %v\n%s", err, out)
}
c := ParseConf(string(out))
if c.Options["ParallelDownloads"][0] != "5" || c.Options["DownloadUser"] == nil || c.Options["Color"] == nil || c.Options["VerbosePkgLists"] == nil {
t.Fatalf("pacman-conf does not read the options as written: %v", c.Options)
}
}
func TestReflectorWritesTheListPacmanReads(t *testing.T) {
content := templateText(t, manifest(t).resource(t, "mirrors"))
opts := map[string]string{}
for _, l := range strings.Split(content, "\n") {
if l == "" || strings.HasPrefix(l, "#") {
continue
}
k, v, _ := strings.Cut(l, " ")
opts[k] = v
}
if opts["--save"] != Mirrorlist || opts["--protocol"] != "https" || opts["--latest"] != "20" || opts["--sort"] != "rate" || opts["--country"] == "" {
t.Fatalf("%v", opts)
}
if manifest(t).resource(t, "mirrors")["path"] != ReflectorConf {
t.Fatal("reflector reads its options from " + ReflectorConf)
}
}
func TestTheReflectorPackageIsDeclaredBeforeTheFileItShips(t *testing.T) {
order := map[string]int{}
for i, r := range manifest(t).Resources {
order[r["id"].(string)] = i
}
if order["reflector"] > order["mirrors"] || order["contrib"] > order["cache-cleaning"] || order["reflector"] > order["mirror-refresh"] {
t.Fatalf("a package's file and timer come after the package: %v", order)
}
}