From 3bdab29a5a0ed0f15cbfdb4738a321f968ac9467 Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 13 Oct 2026 02:02:20 +0200 Subject: [PATCH] Add the replays of issues 279 and 412, written with only what the catalogue had before each fix So the lab's prover can lay them over the commit before mesh-controller #260 and #267 and see them fail there and pass on the fix (hq issues 279, 412). --- internal/catalogue/replay279_test.go | 36 ++++++++++++++++++++++++++++ internal/catalogue/replay412_test.go | 36 ++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 internal/catalogue/replay279_test.go create mode 100644 internal/catalogue/replay412_test.go diff --git a/internal/catalogue/replay279_test.go b/internal/catalogue/replay279_test.go new file mode 100644 index 00000000..c350a4b2 --- /dev/null +++ b/internal/catalogue/replay279_test.go @@ -0,0 +1,36 @@ +package catalogue + +import "testing" + +// Replay of novox/hq issue 279 (2026-10-06): the media managers' recycle-bin folders were declared owned by +// a literal user and group, and on a machine whose puid and pgid settings named another account the apps +// refused them as not writable. A directory's owner could not follow the account its module runs as. Written +// only with what the catalogue had before its fix (mesh-controller #260): Compose, LeftOut, a setting layer, +// and the adoption tests' anchor, so it is laid over the commit before. +func TestReplay279(t *testing.T) { + r := anAdoptedAnchor() + r.Modules = append(r.Modules, Manifest{Module: "sonarr", + Resources: []map[string]any{ + {"id": "recycle-bin", "type": "directory", "path": "/srv/sonarr/recycle", "mode": "0755", + "owner": "${setting:puid}:${setting:pgid}"}, + {"id": "identity", "type": "file", "path": "/srv/sonarr/identity.env", "mode": "0644", + "content": "PUID=${setting:puid}\nPGID=${setting:pgid}\n"}, + }, + }) + with := anchorRendering(false) + with.Settings["sonarr"] = []Layer{{From: "node anchor", Values: map[string]any{"puid": "1001", "pgid": "2000"}}} + composed, err := r.Compose(with) + if err != nil { + t.Fatalf("the machine did not compose: %v", err) + } + if why, left := composed.LeftOut["sonarr"]; left { + t.Fatalf("the module was left out: %s", why) + } + got, ok := byID(composed.Resources)["sonarr.recycle-bin"] + if !ok { + t.Fatal("the recycle bin was not declared") + } + if got["owner"] != "1001:2000" { + t.Fatalf("the recycle bin reaches the node-engine owned by %v, not the account the module runs as (1001:2000)", got["owner"]) + } +} diff --git a/internal/catalogue/replay412_test.go b/internal/catalogue/replay412_test.go new file mode 100644 index 00000000..178667d1 --- /dev/null +++ b/internal/catalogue/replay412_test.go @@ -0,0 +1,36 @@ +package catalogue + +import ( + "strings" + "testing" +) + +// Replay of novox/hq issue 412 (2026-10-10): which module owns /etc/hosts was enforced only after the fact, +// when two modules on one machine declared it; a module that did not claim node-hostname could declare +// /etc/hosts, and a module could claim node-hostname declaring none of its paths, and nothing refused either. +// Written only with what the catalogue had before its fix (mesh-controller #267): CatalogueProblems, Shelf, +// SeatNamed and CanHold, so it is laid over the commit before. +func TestReplay412(t *testing.T) { + stranger := Manifest{Module: "stranger", Version: "1", Resources: []map[string]any{ + {"id": "hosts", "type": "file", "path": "/etc/hosts", "into": "block", "content": "10.0.0.1 x\n"}}} + got := strings.Join(CatalogueProblems(Shelf{"stranger": stranger}), "; ") + if !strings.Contains(got, "node-hostname") { + t.Errorf("a module declaring /etc/hosts without claiming node-hostname was not refused: %q", got) + } + + seat, ok := SeatNamed("node-hostname") + if !ok { + t.Fatal("no node-hostname seat") + } + holder := Manifest{Module: "names", Version: "1", Claims: []Claim{{Name: "node-hostname", Scope: ScopeNode, + Serves: []string{"entries", "add", "remove"}}}} + err := CanHold(holder, seat) + if err == nil { + t.Fatal("a holder of node-hostname declaring neither /etc/hosts nor /etc/hostname was accepted") + } + for _, want := range []string{"/etc/hosts", "/etc/hostname"} { + if !strings.Contains(err.Error(), want) { + t.Errorf("the refusal does not name %s: %v", want, err) + } + } +} -- 2.54.0