package main import ( "context" "os" "path/filepath" "strings" "testing" "time" ) // The fixtures in testdata are the output of OpenZFS 2.4 (the version the home server runs) in the exact // form each command prints it — tab-separated under -H, exact numbers under -p — with the pool, disk and // address names replaced by documentation ones. func fixture(t *testing.T, name string) string { t.Helper() b, err := os.ReadFile(filepath.Join("testdata", name)) if err != nil { t.Fatal(err) } return string(b) } // fake is a machine for a test: a table of command lines and their answers, every call kept. type fake struct { t *testing.T answers map[string]Ran // after runs once a command line was answered: a snapshot's command changes what the list answers. after map[string]func(*fake) calls []string emitted []map[string]any emitErr error files map[string]string now time.Time uid int errors *ErrorMemory } func newFake(t *testing.T) *fake { return &fake{t: t, answers: map[string]Ran{}, after: map[string]func(*fake){}, files: map[string]string{}, now: time.Date(2026, 10, 9, 12, 0, 0, 0, time.Local), uid: 1000, errors: NewErrorMemory()} } func (f *fake) on(line string, out string) { f.answers[line] = Ran{Out: out} } func (f *fake) machine() *Machine { return &Machine{ Run: func(_ context.Context, name string, args ...string) Ran { line := strings.TrimSpace(name + " " + strings.Join(args, " ")) f.calls = append(f.calls, line) r, ok := f.answers[line] if !ok && name == "sudo" && len(args) > 1 { // sudo is there; the command it was asked for is not. r = Ran{Code: 1, Err: "sudo: " + args[1] + ": command not found"} } else if !ok { r = Ran{Code: -1, Err: "exec: \"" + name + "\": executable file not found in $PATH"} } if then, ok := f.after[line]; ok { then(f) } return r }, UID: f.uid, Now: func() time.Time { return f.now }, ReadFile: func(p string) ([]byte, error) { s, ok := f.files[p] if !ok { if strings.HasPrefix(p, os.TempDir()) { // What the code under test wrote into a test's own directory. return os.ReadFile(p) } return nil, os.ErrNotExist } return []byte(s), nil }, Errors: f.errors, Emit: func(eventType string, body any) error { if f.emitErr != nil { return f.emitErr } b := body.(map[string]any) b["_type"] = eventType f.emitted = append(f.emitted, b) return nil }, } } func line(name string, argv []string) string { return name + " " + strings.Join(argv, " ") } // withZFS answers the read commands from the healthy fixtures. func withZFS(t *testing.T) *fake { f := newFake(t) f.on(line("zfs", DatasetsArgv()), fixture(t, "zfs-list.txt")) f.on(line("zfs", SnapshotsArgv()), fixture(t, "zfs-list-snapshots.txt")) f.on(line("zpool", PoolsArgv()), fixture(t, "zpool-list.txt")) f.on(line("zpool", StatusArgv()), fixture(t, "zpool-status.txt")) return f } func ran(f *fake, prefix string) []string { var out []string for _, c := range f.calls { if strings.HasPrefix(c, prefix) { out = append(out, c) } } return out }