Files
mesh-sdk/go/catalogue/environment_items_test.go
T
jschoubben 68aaf235d4 Take the judge out of mesh-sdk: it is mesh-delivery's now (ADR 0578 §4, §7)
Issue 1585, S19: the strict parser, every manifest problem function and the judge's tables (192 declarations,
about 5,200 lines) and their 244 tests leave; composition keeps its own refusals and the helpers it uses. The
catalogue's tests decode the manifests they build. ParseManifest, ParseManifestWith, ParseManifestForInputs,
CatalogueProblems and the rest are gone; mesh-delivery-indexer/judge holds them.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-01 02:36:24 +01:00

142 lines
6.3 KiB
Go

package catalogue
import (
"fmt"
"os"
"path/filepath"
"reflect"
"strings"
"testing"
)
// The session environment is a collection of typed items (design 57 L3, issue 795): a variable is an item with
// kind=variable and name=<NAME>, a PATH entry one with kind=path and at=start|end, each a template whose text is the
// value or the entry. Where node-env is not handed the controller renders the items as the old form was rendered;
// where it is, it names the collection for the client, which fills the same files (mesh-catalog's provisioner client
// holds that half against the same golden files). testdata/environment holds the environment of the catalogue's five
// contributors as the old form composed it, with all five and with zsh alone.
type envDecl struct{ module, item, name, value string }
func environmentOfTheCatalogue() []envDecl {
return []envDecl{
{"adwaita", "gtk-theme", "GTK_THEME", "Adwaita:dark"},
{"adwaita", "gtk2-rc-files", "GTK2_RC_FILES", "/usr/share/themes/Adwaita-dark/gtk-2.0/gtkrc"},
{"adwaita", "qt-qpa-platformtheme", "QT_QPA_PLATFORMTHEME", "qt6ct"},
{"adwaita", "qt-style-override", "QT_STYLE_OVERRIDE", "Fusion"},
{"adwaita", "qt-select", "QT_SELECT", "6"},
{"adwaita", "xcursor-theme", "XCURSOR_THEME", "Adwaita"},
{"adwaita", "xcursor-size", "XCURSOR_SIZE", "24"},
{"clipmenu", "cm-selections", "CM_SELECTIONS", "clipboard"},
{"clipmenu", "cm-max-clips", "CM_MAX_CLIPS", "500"},
{"clipmenu", "cm-histlength", "CM_HISTLENGTH", "15"},
{"i3", "xdg-current-desktop", "XDG_CURRENT_DESKTOP", "i3"},
{"i3", "xdg-session-desktop", "XDG_SESSION_DESKTOP", "i3"},
{"xterm", "terminal", "TERMINAL", "xterm"},
{"zsh", "editor", "EDITOR", "vim"},
{"zsh", "visual", "VISUAL", "vim"},
{"zsh", "xdg-config-home", "XDG_CONFIG_HOME", "${machine:account-home}/.config"},
{"zsh", "path-local-bin", "path:10", "${machine:account-home}/.local/bin"},
{"zsh", "path-scripts", "path:20", "${machine:account-home}/scripts"},
{"zsh", "path-scripts-bin", "path:30", "${machine:account-home}/scripts/bin"},
}
}
const environmentCollector = `{"module":"node-env","version":"1","moved":true,
"claims":[{"name":"node-environment","scope":"node"}],
"build":{"artifacts":[{"name":"files","kind":"archive","from":"files"}]},
"resources":[{"id":"posix","type":"file","path":"/home/op/.config/mesh/environment.sh","template":"files/posix"},
{"id":"systemd","type":"file","path":"/home/op/.config/environment.d/50-mesh.conf","template":"files/systemd"}]}`
// nodeEnvWith is node-env, its file placing the environment as plan says, with the declared contributors' items.
func nodeEnvWith(t *testing.T, decls []envDecl, plan string) Resolution {
t.Helper()
collector := held(t, environmentCollector, builtWith("node-env", map[string]string{
"posix": "# header\n" + plan, "systemd": "${environment:systemd}"}))
mods := []Manifest{collector}
order := []string{"node-env"}
byModule := map[string][]envDecl{}
var modules []string
for _, d := range decls {
if byModule[d.module] == nil {
modules = append(modules, d.module)
}
byModule[d.module] = append(byModule[d.module], d)
}
for _, name := range modules {
var items []string
texts := map[string]string{}
for _, d := range byModule[name] {
attrs := fmt.Sprintf(`{"kind":"variable","name":%q}`, d.name)
if order, isPath := strings.CutPrefix(d.name, "path:"); isPath {
attrs = fmt.Sprintf(`{"kind":"path","at":"start","order":%q}`, order)
}
items = append(items, fmt.Sprintf(`{"collection":"environment","name":%q,"attributes":%s,"template":"files/env-%s"}`,
d.item, attrs, d.item))
texts["env-"+d.item] = d.value + "\n"
}
raw := fmt.Sprintf(`{"module":%q,"version":"1","moved":true,"items":[%s],
"build":{"artifacts":[{"name":"files","kind":"archive","from":"files"}]}}`, name, strings.Join(items, ","))
mods = append(mods, held(t, raw, builtWith(name, texts)))
order = append(order, name)
}
r, err := Resolve(shelf(mods...), order, workstation(), World{})
if err != nil {
t.Fatal(err)
}
r.Account = "op"
return r
}
func TestTheEnvironmentOfTheCatalogueIsTheSameHandedOrNot(t *testing.T) {
read := func(name string) string {
b, err := os.ReadFile(filepath.Join("testdata", "environment", name))
if err != nil {
t.Fatal(err)
}
return string(b)
}
for name, only := range map[string]string{"all": "", "zsh": "zsh"} {
var decls []envDecl
for _, d := range environmentOfTheCatalogue() {
if only == "" || d.module == only {
decls = append(decls, d)
}
}
// Named as the collector names it, and as the old placeholder does.
for _, plan := range []string{"${collected:environment:posix}", "${environment:posix}"} {
r := nodeEnvWith(t, decls, plan)
// Not handed: the controller renders each moved contributor's items as the old form was rendered.
if got, want := composedFile(t, r, "node-env.posix"), "# header\n"+read(name+".posix"); got != want {
t.Errorf("%s, %s, not handed, the POSIX file is\n%s\nwant\n%s", name, plan, got, want)
}
if got, want := composedFile(t, r, "node-env.systemd"), read(name+".systemd"); got != want {
t.Errorf("%s, %s, not handed, the environment.d file is\n%s\nwant\n%s", name, plan, got, want)
}
// Handed: one collection, named for the client; each contributor's plan names its items.
c, _ := composeWith(t, r, handingTo("store:5000"))
if got := c.Plans["node-env"].Collections; !reflect.DeepEqual(got, []PlanCollection{{Name: "environment", Comment: "#"}}) {
t.Errorf("%s: the plan collects %+v", name, got)
}
items, got := len(decls), 0
for m := range c.Plans {
got += len(c.Plans[m].Items)
}
if got != items {
t.Errorf("%s: the plans name %d items, want %d", name, got, items)
}
}
}
}
func TestTwoModulesSettingOneVariableAreRefused(t *testing.T) {
// A module that sets a variable twice, in two modules, is refused as the old form refused it.
a := Manifest{Module: "a", Items: []Item{{Collection: "environment", Content: "1\n",
Attributes: map[string]string{"kind": "variable", "name": "X"}}}}
b := Manifest{Module: "b", Items: []Item{{Collection: "environment", Content: "2\n",
Attributes: map[string]string{"kind": "variable", "name": "X"}}}}
if err := variablesSetOnce([]Manifest{a, b}); err == nil || !strings.Contains(err.Error(), "both set X") {
t.Errorf("two modules setting X: %v", err)
}
}