Skip to content

Commit 550e6ae

Browse files
committed
test: cover extension-tolerant repo fallback and stop browser leak in tests
- add tests for dotGitFilesystems, dotGitFileToFilesystem, dotGitCommonDirectory and the worktree + unknown-extension fallback path, restoring coverage above the 70% threshold (89.3%) - mock the browser in the branch-name-error root command case and restore BrowserCommand after initConfig tests, so running the test suite no longer launches a real browser (e.g. Firefox)
1 parent f6cb85f commit 550e6ae

2 files changed

Lines changed: 236 additions & 0 deletions

File tree

cmd/git_additional_test.go

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package cmd
22

33
import (
4+
"errors"
45
"os"
56
"path/filepath"
67
"strings"
78
"testing"
89

10+
"github.com/go-git/go-billy/v5/osfs"
11+
"github.com/go-git/go-git/v5"
912
"github.com/go-git/go-git/v5/plumbing"
1013
"github.com/zhaochunqi/git-open/internal/testhelper"
1114
)
@@ -75,3 +78,223 @@ func Test_resolveWebURL_WorktreeConfigExtension(t *testing.T) {
7578
t.Errorf("getBranchName() = %q, want %q", branch, "main")
7679
}
7780
}
81+
82+
// Regression test: the extension-tolerant fallback must also work from a
83+
// linked worktree, where .git is a file pointing to a git directory that
84+
// references a commondir.
85+
func Test_openRepositoryToleratingExtensions_Worktree(t *testing.T) {
86+
worktreeDir, cleanup := testhelper.SetupTestWorktree(t, "https://github.com/zhaochunqi/git-open.git", "feature-branch")
87+
defer cleanup()
88+
89+
// Locate the main repository's common git directory through the
90+
// worktree's .git file and its commondir pointer.
91+
b, err := os.ReadFile(filepath.Join(worktreeDir, ".git"))
92+
if err != nil {
93+
t.Fatalf("read .git file failed: %v", err)
94+
}
95+
gitdir := strings.TrimSpace(strings.TrimPrefix(string(b), "gitdir: "))
96+
cb, err := os.ReadFile(filepath.Join(gitdir, "commondir"))
97+
if err != nil {
98+
t.Fatalf("read commondir failed: %v", err)
99+
}
100+
commonDir := strings.TrimSpace(string(cb))
101+
if !filepath.IsAbs(commonDir) {
102+
commonDir = filepath.Join(gitdir, commonDir)
103+
}
104+
105+
// Enable an extension unknown to go-git in the common config, so the
106+
// strict PlainOpenWithOptions path fails and the fallback is exercised.
107+
configPath := filepath.Join(commonDir, "config")
108+
f, err := os.OpenFile(configPath, os.O_APPEND|os.O_WRONLY, 0644)
109+
if err != nil {
110+
t.Fatalf("open config failed: %v", err)
111+
}
112+
if _, err := f.WriteString("[extensions]\n\tworktreeConfig = true\n"); err != nil {
113+
f.Close()
114+
t.Fatalf("append extensions section failed: %v", err)
115+
}
116+
if err := f.Close(); err != nil {
117+
t.Fatalf("close config failed: %v", err)
118+
}
119+
120+
repo, err := getCurrentGitDirectory()
121+
if err != nil {
122+
t.Fatalf("getCurrentGitDirectory() error in worktree with extensions = %v", err)
123+
}
124+
125+
branch, err := getBranchName(repo)
126+
if err != nil {
127+
t.Fatalf("getBranchName() error = %v", err)
128+
}
129+
if branch != "feature-branch" {
130+
t.Errorf("getBranchName() = %q, want %q", branch, "feature-branch")
131+
}
132+
133+
remoteURL, err := getRemoteURL(repo)
134+
if err != nil {
135+
t.Fatalf("getRemoteURL() error = %v", err)
136+
}
137+
if remoteURL != "https://github.com/zhaochunqi/git-open.git" {
138+
t.Errorf("getRemoteURL() = %q, want %q", remoteURL, "https://github.com/zhaochunqi/git-open.git")
139+
}
140+
}
141+
142+
func Test_dotGitFilesystems_NotFound(t *testing.T) {
143+
// A plain temporary directory has no .git anywhere up to the filesystem
144+
// root, so the lookup must report ErrRepositoryNotExists.
145+
tmpDir := t.TempDir()
146+
147+
_, _, err := dotGitFilesystems(tmpDir)
148+
if !errors.Is(err, git.ErrRepositoryNotExists) {
149+
t.Errorf("dotGitFilesystems() error = %v, want %v", err, git.ErrRepositoryNotExists)
150+
}
151+
}
152+
153+
func Test_dotGitFileToFilesystem(t *testing.T) {
154+
absGitdir := t.TempDir()
155+
156+
tests := []struct {
157+
name string
158+
content string
159+
wantRoot string
160+
wantErr bool
161+
}{
162+
{
163+
name: "relative gitdir",
164+
content: "gitdir: .git-real\n",
165+
wantRoot: ".git-real",
166+
},
167+
{
168+
name: "absolute gitdir",
169+
content: "gitdir: " + absGitdir + "\n",
170+
wantRoot: absGitdir,
171+
},
172+
{
173+
name: "missing gitdir prefix",
174+
content: "not a gitdir pointer\n",
175+
wantErr: true,
176+
},
177+
}
178+
179+
for _, tt := range tests {
180+
t.Run(tt.name, func(t *testing.T) {
181+
tmpDir := t.TempDir()
182+
if err := os.WriteFile(filepath.Join(tmpDir, ".git"), []byte(tt.content), 0644); err != nil {
183+
t.Fatal(err)
184+
}
185+
186+
fs, err := dotGitFileToFilesystem(tmpDir, osfs.New(tmpDir))
187+
if (err != nil) != tt.wantErr {
188+
t.Fatalf("dotGitFileToFilesystem() error = %v, wantErr %v", err, tt.wantErr)
189+
}
190+
if tt.wantErr {
191+
return
192+
}
193+
194+
want := tt.wantRoot
195+
if !filepath.IsAbs(want) {
196+
want = filepath.Join(tmpDir, want)
197+
}
198+
// osfs.Root resolves symlinks (e.g. /var -> /private/var on macOS).
199+
if resolved, err := filepath.EvalSymlinks(want); err == nil {
200+
want = resolved
201+
}
202+
if fs.Root() != want {
203+
t.Errorf("dotGitFileToFilesystem() root = %q, want %q", fs.Root(), want)
204+
}
205+
})
206+
}
207+
}
208+
209+
func Test_dotGitFileToFilesystem_MissingFile(t *testing.T) {
210+
tmpDir := t.TempDir()
211+
212+
_, err := dotGitFileToFilesystem(tmpDir, osfs.New(tmpDir))
213+
if err == nil {
214+
t.Error("dotGitFileToFilesystem() expected error for missing .git file, got nil")
215+
}
216+
}
217+
218+
func Test_dotGitCommonDirectory(t *testing.T) {
219+
t.Run("no commondir file", func(t *testing.T) {
220+
fs := osfs.New(t.TempDir())
221+
common, err := dotGitCommonDirectory(fs)
222+
if err != nil {
223+
t.Fatalf("dotGitCommonDirectory() error = %v", err)
224+
}
225+
if common != nil {
226+
t.Errorf("dotGitCommonDirectory() = %v, want nil", common)
227+
}
228+
})
229+
230+
t.Run("empty commondir file", func(t *testing.T) {
231+
tmpDir := t.TempDir()
232+
if err := os.WriteFile(filepath.Join(tmpDir, "commondir"), nil, 0644); err != nil {
233+
t.Fatal(err)
234+
}
235+
common, err := dotGitCommonDirectory(osfs.New(tmpDir))
236+
if err != nil {
237+
t.Fatalf("dotGitCommonDirectory() error = %v", err)
238+
}
239+
if common != nil {
240+
t.Errorf("dotGitCommonDirectory() = %v, want nil", common)
241+
}
242+
})
243+
244+
t.Run("relative commondir", func(t *testing.T) {
245+
tmpDir := t.TempDir()
246+
gitdir := filepath.Join(tmpDir, "gitdir")
247+
commonDir := filepath.Join(tmpDir, "common")
248+
for _, dir := range []string{gitdir, commonDir} {
249+
if err := os.MkdirAll(dir, 0755); err != nil {
250+
t.Fatal(err)
251+
}
252+
}
253+
if err := os.WriteFile(filepath.Join(gitdir, "commondir"), []byte("../common\n"), 0644); err != nil {
254+
t.Fatal(err)
255+
}
256+
common, err := dotGitCommonDirectory(osfs.New(gitdir))
257+
if err != nil {
258+
t.Fatalf("dotGitCommonDirectory() error = %v", err)
259+
}
260+
// osfs.Root resolves symlinks (e.g. /var -> /private/var on macOS).
261+
wantDir, err := filepath.EvalSymlinks(commonDir)
262+
if err != nil {
263+
t.Fatal(err)
264+
}
265+
if common == nil || common.Root() != wantDir {
266+
t.Errorf("dotGitCommonDirectory() root = %v, want %q", common, wantDir)
267+
}
268+
})
269+
270+
t.Run("absolute commondir", func(t *testing.T) {
271+
tmpDir := t.TempDir()
272+
commonDir := t.TempDir()
273+
if err := os.WriteFile(filepath.Join(tmpDir, "commondir"), []byte(commonDir+"\n"), 0644); err != nil {
274+
t.Fatal(err)
275+
}
276+
common, err := dotGitCommonDirectory(osfs.New(tmpDir))
277+
if err != nil {
278+
t.Fatalf("dotGitCommonDirectory() error = %v", err)
279+
}
280+
// osfs.Root resolves symlinks (e.g. /var -> /private/var on macOS).
281+
wantDir, err := filepath.EvalSymlinks(commonDir)
282+
if err != nil {
283+
t.Fatal(err)
284+
}
285+
if common == nil || common.Root() != wantDir {
286+
t.Errorf("dotGitCommonDirectory() root = %v, want %q", common, wantDir)
287+
}
288+
})
289+
290+
t.Run("commondir does not exist", func(t *testing.T) {
291+
tmpDir := t.TempDir()
292+
if err := os.WriteFile(filepath.Join(tmpDir, "commondir"), []byte("missing\n"), 0644); err != nil {
293+
t.Fatal(err)
294+
}
295+
_, err := dotGitCommonDirectory(osfs.New(tmpDir))
296+
if !errors.Is(err, git.ErrRepositoryIncomplete) {
297+
t.Errorf("dotGitCommonDirectory() error = %v, want %v", err, git.ErrRepositoryIncomplete)
298+
}
299+
})
300+
}

cmd/root_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,13 @@ func Test_Execute(t *testing.T) {
163163
}
164164

165165
func Test_initConfig(t *testing.T) {
166+
// Restore the global BrowserCommand so a config file with a browser
167+
// setting does not leak into later tests and launch a real browser.
168+
originalBrowserCommand := BrowserCommand
169+
t.Cleanup(func() {
170+
BrowserCommand = originalBrowserCommand
171+
})
172+
166173
tests := []struct {
167174
name string
168175
wantErr bool
@@ -290,6 +297,12 @@ func Test_rootCmd_ErrorHandling(t *testing.T) {
290297
getBranchNameFunc = func(repo *git.Repository) (string, error) {
291298
return "", errors.New("branch name error")
292299
}
300+
301+
// The command succeeds and opens the main page; mock the
302+
// browser so the test does not launch a real one.
303+
OpenURLInBrowser = func(url string) error {
304+
return nil
305+
}
293306
},
294307
wantErr: false, // Branch name error should not cause command to fail, opens main page
295308
},

0 commit comments

Comments
 (0)