package hook_test import ( "os" "path/filepath" "strings" "testing" "git.zb-server.de/Sebi/ssh-netbox-wrapper/internal/hook" ) func TestAliasesPath_UnderCacheDir(t *testing.T) { dir := t.TempDir() orig := os.Getenv("XDG_CACHE_HOME") os.Setenv("XDG_CACHE_HOME", dir) defer os.Setenv("XDG_CACHE_HOME", orig) p := hook.AliasesPath() if !strings.HasPrefix(p, dir) { t.Errorf("AliasesPath should be under XDG_CACHE_HOME, got %q", p) } if !strings.HasSuffix(p, "aliases.sh") { t.Errorf("AliasesPath should end with aliases.sh, got %q", p) } } func TestFishAliasesPath_UnderConfigDir(t *testing.T) { dir := t.TempDir() orig := os.Getenv("XDG_CONFIG_HOME") os.Setenv("XDG_CONFIG_HOME", dir) defer os.Setenv("XDG_CONFIG_HOME", orig) p := hook.FishAliasesPath() if !strings.HasPrefix(p, dir) { t.Errorf("FishAliasesPath should be under XDG_CONFIG_HOME, got %q", p) } if !strings.HasSuffix(p, "netssh.fish") { t.Errorf("FishAliasesPath should end with netssh.fish, got %q", p) } } // TestWriteAliasFiles_ShFile verifies the bash/zsh file is always written. func TestWriteAliasFiles_ShFile(t *testing.T) { cacheDir := t.TempDir() orig := os.Getenv("XDG_CACHE_HOME") os.Setenv("XDG_CACHE_HOME", cacheDir) defer os.Setenv("XDG_CACHE_HOME", orig) entries := []hook.AliasEntry{ {Name: "web01", Host: "web01.example.com"}, {Name: "db01", Host: "db01.example.com"}, } if err := hook.WriteAliasFiles(entries); err != nil { t.Fatalf("WriteAliasFiles: %v", err) } data, err := os.ReadFile(hook.AliasesPath()) if err != nil { t.Fatalf("aliases.sh not created: %v", err) } content := string(data) for _, want := range []string{ "alias web01='netssh web01.example.com'", "alias db01='netssh db01.example.com'", } { if !strings.Contains(content, want) { t.Errorf("aliases.sh missing %q\ncontent:\n%s", want, content) } } } // TestWriteAliasFiles_FishFile verifies the fish file is written when fish config dir exists. func TestWriteAliasFiles_FishFile(t *testing.T) { configDir := t.TempDir() orig := os.Getenv("XDG_CONFIG_HOME") os.Setenv("XDG_CONFIG_HOME", configDir) defer os.Setenv("XDG_CONFIG_HOME", orig) // Create the fish config directory so WriteAliasFiles knows fish is set up. fishDir := filepath.Join(configDir, "fish") os.MkdirAll(fishDir, 0o755) cacheDir := t.TempDir() origCache := os.Getenv("XDG_CACHE_HOME") os.Setenv("XDG_CACHE_HOME", cacheDir) defer os.Setenv("XDG_CACHE_HOME", origCache) entries := []hook.AliasEntry{{Name: "web01", Host: "web01.example.com"}} if err := hook.WriteAliasFiles(entries); err != nil { t.Fatalf("WriteAliasFiles: %v", err) } data, err := os.ReadFile(hook.FishAliasesPath()) if err != nil { t.Fatalf("netssh.fish not created: %v", err) } if !strings.Contains(string(data), "alias web01 'netssh web01.example.com'") { t.Errorf("fish alias file missing expected line\ncontent:\n%s", string(data)) } } // TestWriteAliasFiles_SkipsFishWhenNotConfigured verifies that fish file is NOT written // when fish is not set up (no ~/.config/fish directory). func TestWriteAliasFiles_SkipsFishWhenNotConfigured(t *testing.T) { configDir := t.TempDir() // no fish subdir orig := os.Getenv("XDG_CONFIG_HOME") os.Setenv("XDG_CONFIG_HOME", configDir) defer os.Setenv("XDG_CONFIG_HOME", orig) cacheDir := t.TempDir() origCache := os.Getenv("XDG_CACHE_HOME") os.Setenv("XDG_CACHE_HOME", cacheDir) defer os.Setenv("XDG_CACHE_HOME", origCache) entries := []hook.AliasEntry{{Name: "web01", Host: "web01.example.com"}} if err := hook.WriteAliasFiles(entries); err != nil { t.Fatalf("WriteAliasFiles: %v", err) } if _, err := os.Stat(hook.FishAliasesPath()); err == nil { t.Error("fish alias file should not be created when fish is not configured") } } // TestWriteAliasFiles_EmptyEntries verifies an empty (but valid) file is written. func TestWriteAliasFiles_EmptyEntries(t *testing.T) { cacheDir := t.TempDir() orig := os.Getenv("XDG_CACHE_HOME") os.Setenv("XDG_CACHE_HOME", cacheDir) defer os.Setenv("XDG_CACHE_HOME", orig) if err := hook.WriteAliasFiles(nil); err != nil { t.Fatalf("WriteAliasFiles with nil entries: %v", err) } data, err := os.ReadFile(hook.AliasesPath()) if err != nil { t.Fatalf("aliases.sh not created: %v", err) } // Should contain only the header comment, no alias lines. if strings.Contains(string(data), "alias ") { t.Error("aliases.sh should have no alias lines for empty entries") } } // --- InstallAliasesSource --- func TestInstallAliasesSource_Fresh(t *testing.T) { profile := writeProfile(t, "existing line\n") installed, err := hook.InstallAliasesSource(profile) if err != nil { t.Fatalf("InstallAliasesSource: %v", err) } if !installed { t.Error("should report installed=true on first install") } data, _ := os.ReadFile(profile) if !strings.Contains(string(data), hook.AliasesMarker) { t.Error("profile should contain aliases source line") } } func TestInstallAliasesSource_Idempotent(t *testing.T) { profile := writeProfile(t, "") hook.InstallAliasesSource(profile) installed, err := hook.InstallAliasesSource(profile) if err != nil { t.Fatalf("second InstallAliasesSource: %v", err) } if installed { t.Error("second call should report installed=false") } data, _ := os.ReadFile(profile) if count := strings.Count(string(data), hook.AliasesMarker); count != 1 { t.Errorf("aliases source line should appear once, got %d", count) } } // TestUninstall_AlsoRemovesAliasesSourceLine verifies that Uninstall removes both lines. func TestUninstall_AlsoRemovesAliasesSourceLine(t *testing.T) { content := "export PATH=$PATH\n" + hook.Line + "\n" + hook.AliasesSourceLine + "\n" + "export FOO=bar\n" profile := writeProfile(t, content) removed, err := hook.Uninstall(profile) if err != nil { t.Fatalf("Uninstall: %v", err) } if !removed { t.Error("should report removed=true") } data, _ := os.ReadFile(profile) s := string(data) if strings.Contains(s, hook.Marker) { t.Error("shell-init line should be removed") } if strings.Contains(s, hook.AliasesMarker) { t.Error("aliases source line should be removed") } if !strings.Contains(s, "export PATH") || !strings.Contains(s, "export FOO") { t.Error("other content should be preserved") } }