Adds shell integration for automatic Gas Town context detection. Features: - `gt enable` / `gt disable` - Global on/off switch - `gt shell install|remove|status` - Shell integration management - `gt rig quick-add [path]` - One-command project setup - `gt uninstall` - Clean removal with options - Shell hook auto-sets GT_TOWN_ROOT/GT_RIG on cd Implementation: - XDG-compliant state storage (~/.local/state/gastown/) - Safe RC file manipulation with block markers - Environment overrides (GASTOWN_DISABLED/ENABLED) - Doctor check for global state validation Co-authored-by: Sohail Mohammad <sohailm25@gmail.com>
133 lines
3.0 KiB
Go
133 lines
3.0 KiB
Go
// ABOUTME: Tests for shell integration install/remove functionality.
|
|
// ABOUTME: Verifies RC file manipulation and hook script creation.
|
|
|
|
package shell
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestDetectShell(t *testing.T) {
|
|
tests := []struct {
|
|
shellEnv string
|
|
want string
|
|
}{
|
|
{"/bin/zsh", "zsh"},
|
|
{"/usr/bin/zsh", "zsh"},
|
|
{"/bin/bash", "bash"},
|
|
{"/usr/bin/bash", "bash"},
|
|
{"", "zsh"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.shellEnv, func(t *testing.T) {
|
|
orig := os.Getenv("SHELL")
|
|
defer os.Setenv("SHELL", orig)
|
|
|
|
os.Setenv("SHELL", tt.shellEnv)
|
|
got := DetectShell()
|
|
if got != tt.want {
|
|
t.Errorf("DetectShell() = %q, want %q", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRCFilePath(t *testing.T) {
|
|
home, _ := os.UserHomeDir()
|
|
|
|
tests := []struct {
|
|
shell string
|
|
want string
|
|
}{
|
|
{"zsh", filepath.Join(home, ".zshrc")},
|
|
{"bash", filepath.Join(home, ".bashrc")},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.shell, func(t *testing.T) {
|
|
got := RCFilePath(tt.shell)
|
|
if got != tt.want {
|
|
t.Errorf("RCFilePath(%q) = %q, want %q", tt.shell, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAddRemoveFromRCFile(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
rcPath := filepath.Join(tmpDir, ".zshrc")
|
|
|
|
originalContent := "# existing content\nalias foo=bar\n"
|
|
if err := os.WriteFile(rcPath, []byte(originalContent), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := addToRCFile(rcPath); err != nil {
|
|
t.Fatalf("addToRCFile() error = %v", err)
|
|
}
|
|
|
|
data, err := os.ReadFile(rcPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
content := string(data)
|
|
|
|
if !strings.Contains(content, markerStart) {
|
|
t.Error("RC file should contain start marker")
|
|
}
|
|
if !strings.Contains(content, markerEnd) {
|
|
t.Error("RC file should contain end marker")
|
|
}
|
|
if !strings.Contains(content, "shell-hook.sh") {
|
|
t.Error("RC file should source shell-hook.sh")
|
|
}
|
|
if !strings.Contains(content, "# existing content") {
|
|
t.Error("RC file should preserve original content")
|
|
}
|
|
|
|
if err := removeFromRCFile(rcPath); err != nil {
|
|
t.Fatalf("removeFromRCFile() error = %v", err)
|
|
}
|
|
|
|
data, err = os.ReadFile(rcPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
content = string(data)
|
|
|
|
if strings.Contains(content, markerStart) {
|
|
t.Error("RC file should not contain start marker after removal")
|
|
}
|
|
if strings.Contains(content, markerEnd) {
|
|
t.Error("RC file should not contain end marker after removal")
|
|
}
|
|
if !strings.Contains(content, "# existing content") {
|
|
t.Error("RC file should preserve original content after removal")
|
|
}
|
|
}
|
|
|
|
func TestUpdateRCFile(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
rcPath := filepath.Join(tmpDir, ".zshrc")
|
|
|
|
if err := addToRCFile(rcPath); err != nil {
|
|
t.Fatalf("initial addToRCFile() error = %v", err)
|
|
}
|
|
|
|
if err := addToRCFile(rcPath); err != nil {
|
|
t.Fatalf("second addToRCFile() error = %v", err)
|
|
}
|
|
|
|
data, _ := os.ReadFile(rcPath)
|
|
content := string(data)
|
|
|
|
startCount := strings.Count(content, markerStart)
|
|
if startCount != 1 {
|
|
t.Errorf("RC file has %d start markers, want 1", startCount)
|
|
}
|
|
}
|