From 3293f6ddcdb55bb4cb79b256cacdb17bcd213077 Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 2 Nov 2026 19:21:10 +0100 Subject: [PATCH] Take the environment's rendering integration tests with the code they test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ADR 0593 ยง7: the environment's rendering left mesh-sdk for the node-engine, so its two integration tests (the POSIX environment sourced twice, and the service manager reading the systemd rendering) move with it. Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL --- .../environment_into_integration_test.go | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 internal/compose/resolution/environment_into_integration_test.go diff --git a/internal/compose/resolution/environment_into_integration_test.go b/internal/compose/resolution/environment_into_integration_test.go new file mode 100644 index 0000000..d63fa7e --- /dev/null +++ b/internal/compose/resolution/environment_into_integration_test.go @@ -0,0 +1,91 @@ +//go:build integration + +package resolution + +import ( + "os" + "os/exec" + "path/filepath" + "strings" + "testing" +) + +func TestThePOSIXEnvironmentSourcedTwiceLeavesPATHAsOnce(t *testing.T) { + sh, err := exec.LookPath("sh") + if err != nil { + t.Skip("no sh on this machine") + } + script := "PATH=/usr/bin:/bin\n" + composedPOSIX + "once=$PATH\n" + composedPOSIX + + `[ "$PATH" = "$once" ] || { echo "changed: $once -> $PATH"; exit 1; }` + "\n" + + `echo "$PATH"; echo "$GOPATH"` + out, err := exec.Command(sh, "-c", script).CombinedOutput() + if err != nil { + t.Fatalf("sourcing twice: %v\n%s", err, out) + } + lines := strings.Split(strings.TrimSpace(string(out)), "\n") + want := "/home/op/go/bin:/usr/local/go/bin:/home/op/.local/bin:/home/op/bin:/usr/bin:/bin:/opt/agent/bin:/opt/scripts" + if lines[0] != want { + t.Fatalf("PATH is %s, not %s", lines[0], want) + } + if lines[1] != "/home/op/go" { + t.Fatalf("GOPATH was not exported: %q", lines[1]) + } + // And an entry the account already has stays where it is, and once. + out, err = exec.Command(sh, "-c", "PATH=/opt/scripts:/usr/bin\n"+composedPOSIX+`echo "$PATH"`).CombinedOutput() + if err != nil { + t.Fatalf("%v\n%s", err, out) + } + if got := strings.TrimSpace(string(out)); got != + "/home/op/go/bin:/usr/local/go/bin:/home/op/.local/bin:/home/op/bin:/opt/scripts:/usr/bin:/opt/agent/bin" { + t.Fatalf("an entry already on PATH was added again or moved: %s", got) + } +} + +func TestTheServiceManagerReadsTheSystemdRenderingAsMeant(t *testing.T) { + generator := "/usr/lib/systemd/user-environment-generators/30-systemd-environment-d-generator" + if _, err := os.Stat(generator); err != nil { + t.Skip("NOT JUDGED: no environment.d generator on this machine, so the service manager's reading " + + "of the rendering is judged only where systemd runs; the rendering itself is held byte for byte " + + "by TestTheEnvironmentRendersForTheServiceManagerByteForByte") + } + const base = "/usr/bin:/bin" + read := func(conf string) map[string]string { + t.Helper() + config := t.TempDir() + if err := os.MkdirAll(filepath.Join(config, "environment.d"), 0o755); err != nil { + t.Fatal(err) + } + if conf != "" { + if err := os.WriteFile(filepath.Join(config, "environment.d", "50-mesh.conf"), []byte(conf), 0o644); err != nil { + t.Fatal(err) + } + } + cmd := exec.Command(generator) + cmd.Env = []string{"PATH=" + base, "HOME=" + config, "XDG_CONFIG_HOME=" + config} + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("%v\n%s", err, out) + } + vars := map[string]string{} + for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") { + if k, v, ok := strings.Cut(line, "="); ok { + vars[k] = v + } + } + return vars + } + machine, read50 := read(""), read(composedSystemd) + + // What the machine's own files do to PATH: nothing, or append to it. One that replaces or prepends + // leaves nothing this test can say about the mesh's file, and says so rather than guess. + added, ok := strings.CutPrefix(machine["PATH"], base) + if machine["PATH"] != "" && !ok { + t.Skipf("NOT JUDGED: this machine's own environment.d sets PATH to %s, not %s with something after it, so "+ + "what the mesh's file adds cannot be told apart from it", machine["PATH"], base) + } + want := "/home/op/go/bin:/usr/local/go/bin:/home/op/.local/bin:/home/op/bin:/usr/bin:/bin:/opt/agent/bin:/opt/scripts" + added + if read50["PATH"] != want || read50["GOPATH"] != "/home/op/go" { + t.Fatalf("the service manager read PATH=%s GOPATH=%s, not PATH=%s GOPATH=/home/op/go (the machine's own "+ + "files add %q to PATH)", read50["PATH"], read50["GOPATH"], want, added) + } +}