diff --git a/modules/pacman/cmd/pacman-tools/keeper_bus_test.go b/modules/pacman/cmd/pacman-tools/keeper_bus_test.go new file mode 100644 index 00000000..f337c7e3 --- /dev/null +++ b/modules/pacman/cmd/pacman-tools/keeper_bus_test.go @@ -0,0 +1,97 @@ +package main + +// What the keeper puts on the bus is only its named fields (novox/hq ADR 0507 ยง1; acceptance 1840/AC7): pacman's +// output holding command lines, environment variables and messages leaves none of them in a value or an event, +// and no command the keeper runs names a package, a foreign one least of all. (busshapes_test.go holds every +// shape to its named fields.) + +import ( + "context" + "encoding/json" + "os" + "path/filepath" + "strings" + "testing" + "time" +) + +func TestPacmanOutputHoldingCommandLinesAndEnvironmentPutsNeitherOnTheBus(t *testing.T) { + dir := filepath.Join("testdata", "pacman-output-with-command-lines-and-environment") + read := func(name string) string { + b, err := os.ReadFile(filepath.Join(dir, name)) + if err != nil { + t.Fatal(err) + } + return string(b) + } + db := &pacmanDB{sl: read("pacman-Sl"), checkup: Ran{Stdout: read("checkupdates")}} + db.set(read("pacman-Q"), read("pacman-Qeq")) + k, states, ev, clock, w := newTestKeeper(db) + ctx, cancel := context.WithCancel(context.Background()) + done := make(chan error, 1) + go func() { done <- k.Run(ctx) }() + for deadline := time.Now().Add(5 * time.Second); time.Now().Before(deadline); time.Sleep(time.Millisecond) { + if e, _ := states.store(StateUpdates).Get("node-a"); e != nil { + break + } + } + clock.Advance(time.Minute) + w.c <- LockReleased + w.c <- MirrorsRewritten + time.Sleep(20 * time.Millisecond) + cancel() + <-done + + var onBus []string + for _, name := range []string{StateInstalled, StateUpdates} { + s := states.store(name) + keys, _ := s.Keys() + for _, key := range keys { + e, _ := s.Get(key) + onBus = append(onBus, name+"."+key+" "+string(e.Value)) + } + } + events := ev.Take() + if len(events) == 0 { + t.Fatal("the first write said nothing") + } + for _, e := range events { + body, _ := json.Marshal(e.body) + onBus = append(onBus, e.name+" "+string(body)) + } + if len(onBus) < 3 { + t.Fatalf("on the bus: %v", onBus) + } + whole := strings.Join(onBus, "\n") + for _, never := range []string{"=", "sudo", "--noconfirm", "/", "rm", "warning", "hunter2", "Synchronizing", "HOME", "TOKEN", "SECRET"} { + if strings.Contains(whole, never) { + t.Errorf("%q reached the bus:\n%s", never, whole) + } + } + var inst InstalledValue + e, _ := states.store(StateInstalled).Get("node-a") + json.Unmarshal(e.Value, &inst) + if !samePackages(inst.Packages, []InstalledPackage{{"glibc", "2.42-1", false, "core"}, {"yay", "12.0-1", true, RepoForeign}, {"zsh", "5.9-1", true, "extra"}}) { + t.Errorf("the packages are read, the rest left out: %+v", inst.Packages) + } + var up UpdatesValue + e, _ = states.store(StateUpdates).Get("node-a") + json.Unmarshal(e.Value, &up) + if len(up.Packages) != 1 || up.Packages[0] != (VersionChange{"glibc", "2.42-1", "2.42-2"}) { + t.Errorf("updates: %+v", up) + } + + // no command the keeper ran names a package; the foreign one least of all. + db.mu.Lock() + defer db.mu.Unlock() + for _, c := range db.commands { + switch c { + case "pacman -Q", "pacman -Qeq", "pacman -Sl", "checkupdates": + default: + t.Errorf("the keeper ran %q", c) + } + if strings.Contains(c, "yay") { + t.Errorf("a command named the foreign package: %q", c) + } + } +} diff --git a/modules/pacman/cmd/pacman-tools/testdata/pacman-output-with-command-lines-and-environment/checkupdates b/modules/pacman/cmd/pacman-tools/testdata/pacman-output-with-command-lines-and-environment/checkupdates new file mode 100644 index 00000000..40c952dc --- /dev/null +++ b/modules/pacman/cmd/pacman-tools/testdata/pacman-output-with-command-lines-and-environment/checkupdates @@ -0,0 +1,4 @@ +glibc 2.42-1 -> 2.42-2 +zsh 5.9-1 -> 5.9-2 PASSWORD=hunter2 +env FOO=bar -> /bin/sh +:: Synchronizing package databases... diff --git a/modules/pacman/cmd/pacman-tools/testdata/pacman-output-with-command-lines-and-environment/pacman-Q b/modules/pacman/cmd/pacman-tools/testdata/pacman-output-with-command-lines-and-environment/pacman-Q new file mode 100644 index 00000000..a8c96383 --- /dev/null +++ b/modules/pacman/cmd/pacman-tools/testdata/pacman-output-with-command-lines-and-environment/pacman-Q @@ -0,0 +1,9 @@ +glibc 2.42-1 +sudo pacman -Syu --noconfirm +yay 12.0-1 +LD_PRELOAD=/tmp/hook.so 1.0-1 +zsh 5.9-1 +bash 5.2-1 HOME=/root +evil 1.0;rm-rf +PATH=/usr/bin:/bin +warning: could not get file information for etc/secret diff --git a/modules/pacman/cmd/pacman-tools/testdata/pacman-output-with-command-lines-and-environment/pacman-Qeq b/modules/pacman/cmd/pacman-tools/testdata/pacman-output-with-command-lines-and-environment/pacman-Qeq new file mode 100644 index 00000000..815d6570 --- /dev/null +++ b/modules/pacman/cmd/pacman-tools/testdata/pacman-output-with-command-lines-and-environment/pacman-Qeq @@ -0,0 +1,3 @@ +yay +zsh +TOKEN=abc diff --git a/modules/pacman/cmd/pacman-tools/testdata/pacman-output-with-command-lines-and-environment/pacman-Sl b/modules/pacman/cmd/pacman-tools/testdata/pacman-output-with-command-lines-and-environment/pacman-Sl new file mode 100644 index 00000000..269f745e --- /dev/null +++ b/modules/pacman/cmd/pacman-tools/testdata/pacman-output-with-command-lines-and-environment/pacman-Sl @@ -0,0 +1,3 @@ +core glibc 2.42-1 [installed] +extra zsh 5.9-1 [installed] +extra SECRET=x 1-1