package zsh import ( "context" "os" "path/filepath" "testing" "gitlab.hive-brain.de/MDietermann/Archduino/internal/config" "gitlab.hive-brain.de/MDietermann/Archduino/internal/module" "gitlab.hive-brain.de/MDietermann/Archduino/internal/platform" ) type fakeRunner struct { runCalls [][]string outCalls [][]string installed map[string]bool } func (f *fakeRunner) Run(_ context.Context, name string, args ...string) error { f.runCalls = append(f.runCalls, append([]string{name}, args...)) return nil } func (f *fakeRunner) Output(_ context.Context, name string, args ...string) ([]byte, error) { f.outCalls = append(f.outCalls, append([]string{name}, args...)) if name == "pacman" && len(args) == 2 && args[0] == "-Q" { if f.installed[args[1]] { return []byte("ok"), nil } return nil, errOne } return nil, nil } type eo struct{} func (eo) Error() string { return "exit 1" } var errOne = eo{} type nopReporter struct{} func (nopReporter) Progress(string, int, int, string) {} func (nopReporter) Log(string, string) {} func TestZshDetectAbsent(t *testing.T) { home := t.TempDir() t.Setenv("HOME", home) r := &fakeRunner{installed: map[string]bool{}} m := New(config.ZshConfig{ OhMyZshRepo: "https://x/omz.git", Plugins: []config.ZshPlugin{{Name: "zsh-autosuggestions", Repo: "https://x/a.git"}}, }, platform.NewPacman(r), platform.GitClient{Runner: r}) st, _ := m.Detect(context.Background()) s := st.(state) if s.ZshInstalled { t.Error("want zsh absent") } if s.OMZInstalled { t.Error("want omz absent") } if len(s.MissingPlugins) != 1 { t.Errorf("want 1 plugin missing, got %v", s.MissingPlugins) } } func TestZshDetectAllPresent(t *testing.T) { home := t.TempDir() t.Setenv("HOME", home) os.MkdirAll(filepath.Join(home, ".oh-my-zsh"), 0o755) os.MkdirAll(filepath.Join(home, ".oh-my-zsh", "custom", "plugins", "zsh-autosuggestions", ".git"), 0o755) r := &fakeRunner{installed: map[string]bool{"zsh": true}} m := New(config.ZshConfig{ OhMyZshRepo: "https://x/omz.git", Plugins: []config.ZshPlugin{{Name: "zsh-autosuggestions", Repo: "https://x/a.git"}}, }, platform.NewPacman(r), platform.GitClient{Runner: r}) st, _ := m.Detect(context.Background()) s := st.(state) if !s.ZshInstalled || !s.OMZInstalled || len(s.MissingPlugins) != 0 { t.Fatalf("want all present, got %+v", s) } } func TestZshPlanApplyInstallsEverything(t *testing.T) { home := t.TempDir() t.Setenv("HOME", home) r := &fakeRunner{installed: map[string]bool{}} m := New(config.ZshConfig{ OhMyZshRepo: "https://x/omz.git", Plugins: []config.ZshPlugin{{Name: "zsh-autosuggestions", Repo: "https://x/a.git"}}, }, platform.NewPacman(r), platform.GitClient{Runner: r}) st, _ := m.Detect(context.Background()) p, _ := m.Plan(context.Background(), st) if len(p.Actions) == 0 { t.Fatal("want non-empty plan") } if err := m.Apply(context.Background(), p, nopReporter{}); err != nil { t.Fatal(err) } sawPacman, sawOMZ, sawPlugin := false, false, false for _, c := range r.runCalls { if len(c) >= 3 && c[0] == "sudo" && c[1] == "pacman" { sawPacman = true } if len(c) >= 4 && c[0] == "git" && c[3] == "https://x/omz.git" { sawOMZ = true } if len(c) >= 4 && c[0] == "git" && c[3] == "https://x/a.git" { sawPlugin = true } } if !sawPacman || !sawOMZ || !sawPlugin { t.Fatalf("missing step; calls=%v", r.runCalls) } } func TestPlanForceReinstallsEverything(t *testing.T) { home := t.TempDir() t.Setenv("HOME", home) os.MkdirAll(filepath.Join(home, ".oh-my-zsh", ".git"), 0o755) os.MkdirAll(filepath.Join(home, ".oh-my-zsh", "custom", "plugins", "zsh-autosuggestions", ".git"), 0o755) r := &fakeRunner{installed: map[string]bool{"zsh": true}} m := New(config.ZshConfig{ OhMyZshRepo: "https://x/omz.git", Plugins: []config.ZshPlugin{{Name: "zsh-autosuggestions", Repo: "https://x/a.git"}}, }, platform.NewPacman(r), platform.GitClient{Runner: r}) plan, err := m.PlanForce(context.Background()) if err != nil { t.Fatalf("PlanForce: %v", err) } if plan.IsEmpty() { t.Fatal("force plan must not be empty") } for _, a := range plan.Actions { if err := a.Exec(context.Background()); err != nil { t.Fatalf("Exec: %v", err) } } sawReinstall, sawOMZ, sawPlugin := false, false, false for _, c := range r.runCalls { if len(c) >= 4 && c[0] == "sudo" && c[1] == "pacman" && c[2] == "-S" && c[3] == "--noconfirm" { sawReinstall = true } if len(c) >= 4 && c[0] == "git" && c[3] == "https://x/omz.git" { sawOMZ = true } if len(c) >= 4 && c[0] == "git" && c[3] == "https://x/a.git" { sawPlugin = true } } if !sawReinstall || !sawOMZ || !sawPlugin { t.Fatalf("missing force action; calls=%v", r.runCalls) } if _, err := os.Stat(filepath.Join(home, ".oh-my-zsh")); !os.IsNotExist(err) { t.Fatalf("oh-my-zsh dir should have been removed; stat err=%v", err) } } var _ module.Module = Module{}