返回 DeepSeek-Reasonix
shell_path_compat_test.go
根目录 / internal / sandbox / shell_path_compat_test.go
1 package sandbox
2
3 import (
4 "os/exec"
5 "reflect"
6 "testing"
7 )
8
9 func TestResolveShellRejectsCrossKindConfiguredPaths(t *testing.T) {
10 paths := map[string]string{
11 "bash": `C:\found\bash.exe`,
12 "powershell": `C:\found\powershell.exe`,
13 "pwsh": `C:\found\pwsh.exe`,
14 }
15 lookPath := func(name string) (string, error) {
16 if path := paths[name]; path != "" {
17 return path, nil
18 }
19 return "", exec.ErrNotFound
20 }
21 exists := func(string) bool { return true }
22 probe := func(string) bool { return true }
23 noWSL := func(string) bool { return false }
24
25 tests := []struct {
26 name string
27 prefer string
28 path string
29 wantKind ShellKind
30 wantPath string
31 wantArgv []string
32 }{
33 {
34 name: "bash path is not relabeled as Windows PowerShell", prefer: "powershell", path: `C:\Git\bin\bash.exe`,
35 wantKind: ShellPowerShell, wantPath: paths["powershell"],
36 wantArgv: []string{paths["powershell"], "-NoProfile", "-NonInteractive", "-Command"},
37 },
38 {
39 name: "sh path is not relabeled as PowerShell 7", prefer: "pwsh", path: `C:\Tools\sh.exe`,
40 wantKind: ShellPowerShell, wantPath: paths["pwsh"],
41 wantArgv: []string{paths["pwsh"], "-NoProfile", "-NonInteractive", "-Command"},
42 },
43 {
44 name: "PowerShell path is not probed as Bash", prefer: "bash", path: `C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe`,
45 wantKind: ShellPowerShell, wantPath: `C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe`,
46 wantArgv: []string{`C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe`, "-NoProfile", "-NonInteractive", "-Command"},
47 },
48 {
49 name: "zsh path is not relabeled as Bash", prefer: "bash", path: `C:\Tools\zsh.exe`,
50 wantKind: ShellPowerShell, wantPath: paths["pwsh"], wantArgv: []string{paths["pwsh"], "-NoProfile", "-NonInteractive", "-Command"},
51 },
52 }
53 for _, tc := range tests {
54 t.Run(tc.name, func(t *testing.T) {
55 got := resolveShell(tc.prefer, tc.path, nil, "windows", lookPath, exists, nil, nil, probe, noWSL)
56 if got.Kind != tc.wantKind || got.Path != tc.wantPath {
57 t.Fatalf("resolved shell = {%s %q}, want {%s %q}", got.Kind, got.Path, tc.wantKind, tc.wantPath)
58 }
59 argv := got.argv("echo ok")
60 if len(argv) < len(tc.wantArgv) || !reflect.DeepEqual(argv[:len(tc.wantArgv)], tc.wantArgv) {
61 t.Fatalf("argv = %q, want prefix %q", argv, tc.wantArgv)
62 }
63 })
64 }
65 }
66
67 func TestResolveShellRejectsExplicitWSLLauncher(t *testing.T) {
68 const (
69 wslBash = `C:\Windows\System32\bash.exe`
70 gitBash = `C:\Program Files\Git\bin\bash.exe`
71 )
72 got := resolveShell(
73 "bash", wslBash, nil, "windows",
74 func(string) (string, error) { return "", exec.ErrNotFound },
75 func(string) bool { return true },
76 []string{gitBash}, nil,
77 func(string) bool { return true },
78 func(path string) bool { return path == wslBash },
79 )
80 if got.Kind != ShellPowerShell || got.Path != "pwsh" {
81 t.Fatalf("resolved shell = {%s %q}, want native PowerShell", got.Kind, got.Path)
82 }
83 }
84
85 func TestResolveShellAutoDoesNotUsePersistedWindowsBashPath(t *testing.T) {
86 const (
87 configured = `E:\Portable\Git\bin\bash.exe`
88 onPath = `C:\Program Files\Git\bin\bash.exe`
89 )
90 got := resolveShell(
91 "auto", configured, nil, "windows",
92 func(name string) (string, error) {
93 if name == "bash" {
94 return onPath, nil
95 }
96 return "", exec.ErrNotFound
97 },
98 func(path string) bool { return path == configured || path == onPath },
99 nil, nil,
100 func(string) bool { return true },
101 func(string) bool { return false },
102 )
103 if got.Kind != ShellPowerShell || got.Path != "pwsh" {
104 t.Fatalf("auto selected a non-native shell: {%s %q}", got.Kind, got.Path)
105 }
106 }
107
108 func TestConfiguredShellPathKeepsUnknownWrappers(t *testing.T) {
109 exists := func(string) bool { return true }
110 noWSL := func(string) bool { return false }
111 for _, tc := range []struct {
112 kind ShellKind
113 path string
114 }{
115 {ShellBash, `C:\Tools\posix-wrapper.exe`},
116 {ShellPowerShell, `C:\Tools\ps-wrapper.exe`},
117 } {
118 if got := configuredShellPath("windows", tc.kind, tc.path, exists, noWSL); got != tc.path {
119 t.Errorf("configured path = %q, want wrapper %q", got, tc.path)
120 }
121 }
122 }
123
124 func TestConfiguredShellPathRejectsUnresolvedGitBashLauncher(t *testing.T) {
125 const launcher = `C:\Tools\Git\git-bash.exe`
126 got := configuredShellPath(
127 "windows", ShellBash, launcher,
128 func(string) bool { return false },
129 func(string) bool { return false },
130 )
131 if got != "" {
132 t.Fatalf("configured path = %q, want the MinTTY launcher rejected", got)
133 }
134 for _, prefer := range []string{"auto", "bash"} {
135 if got := configuredWindowsBashPath(prefer, launcher, func(string) bool { return false }); got != "" {
136 t.Errorf("configuredWindowsBashPath(%q) = %q, want launcher rejected", prefer, got)
137 }
138 }
139 }
140
140 lines GO