From 989ef478b24a9c7df855acd20db4d7ae1d9705ee Mon Sep 17 00:00:00 2001 From: Gleb Glazov Date: Fri, 6 Sep 2024 02:57:45 +0200 Subject: [PATCH] feat: evaluate symlinks when connecting (#150) --- namer/namer.go | 5 +++ namer/namer_test.go | 84 +++++++++++++++++++++++++++++++------------ pathwrap/mock_Path.go | 56 +++++++++++++++++++++++++++++ pathwrap/pathwrap.go | 5 +++ 4 files changed, 127 insertions(+), 23 deletions(-) diff --git a/namer/namer.go b/namer/namer.go index 2de5f37..833bac1 100644 --- a/namer/namer.go +++ b/namer/namer.go @@ -25,6 +25,11 @@ func NewNamer(pathwrap pathwrap.Path, git git.Git) Namer { } func (n *RealNamer) Name(path string) (string, error) { + path, err := n.pathwrap.EvalSymlinks(path) + if err != nil { + return "", err + } + strategies := []func(*RealNamer, string) (string, error){ gitBareName, gitName, diff --git a/namer/namer_test.go b/namer/namer_test.go index 1a664af..3bf1209 100644 --- a/namer/namer_test.go +++ b/namer/namer_test.go @@ -10,31 +10,69 @@ import ( ) func TestFromPath(t *testing.T) { - mockPathwrap := new(pathwrap.MockPath) - mockGit := new(git.MockGit) - n := NewNamer(mockPathwrap, mockGit) - - t.Run("name for git repo", func(t *testing.T) { - mockGit.On("ShowTopLevel", "/Users/josh/c/dotfiles/.config/neovim").Return(true, "/Users/josh/c/dotfiles", nil) - mockGit.On("GitCommonDir", "/Users/josh/c/dotfiles/.config/neovim").Return(true, "", nil) - mockPathwrap.On("Base", "/Users/josh/c/dotfiles").Return("dotfiles") - name, _ := n.Name("/Users/josh/c/dotfiles/.config/neovim") - assert.Equal(t, "dotfiles/_config/neovim", name) - }) + t.Run("when path does not contain a symlink", func(t *testing.T) { + mockPathwrap := new(pathwrap.MockPath) + mockGit := new(git.MockGit) + n := NewNamer(mockPathwrap, mockGit) + + t.Run("name for git repo", func(t *testing.T) { + mockPathwrap.On("EvalSymlinks", "/Users/josh/config/dotfiles/.config/neovim").Return("/Users/josh/config/dotfiles/.config/neovim", nil) + mockGit.On("ShowTopLevel", "/Users/josh/config/dotfiles/.config/neovim").Return(true, "/Users/josh/config/dotfiles", nil) + mockGit.On("GitCommonDir", "/Users/josh/config/dotfiles/.config/neovim").Return(true, "", nil) + mockPathwrap.On("Base", "/Users/josh/config/dotfiles").Return("dotfiles") + name, _ := n.Name("/Users/josh/config/dotfiles/.config/neovim") + assert.Equal(t, "dotfiles/_config/neovim", name) + }) + + t.Run("name for git worktree", func(t *testing.T) { + mockPathwrap.On("EvalSymlinks", "/Users/josh/config/sesh/main").Return("/Users/josh/config/sesh/main", nil) + mockGit.On("ShowTopLevel", "/Users/josh/config/sesh/main").Return(true, "/Users/josh/config/sesh/main", nil) + mockGit.On("GitCommonDir", "/Users/josh/config/sesh/main").Return(true, "/Users/josh/config/sesh/.bare", nil) + mockPathwrap.On("Base", "/Users/josh/config/sesh").Return("sesh") + name, _ := n.Name("/Users/josh/config/sesh/main") + assert.Equal(t, "sesh/main", name) + }) - t.Run("name for git worktree", func(t *testing.T) { - mockGit.On("ShowTopLevel", "/Users/josh/c/sesh/main").Return(true, "/Users/josh/c/sesh/main", nil) - mockGit.On("GitCommonDir", "/Users/josh/c/sesh/main").Return(true, "/Users/josh/c/sesh/.bare", nil) - mockPathwrap.On("Base", "/Users/josh/c/sesh").Return("sesh") - name, _ := n.Name("/Users/josh/c/sesh/main") - assert.Equal(t, "sesh/main", name) + t.Run("returns base on non-git dir", func(t *testing.T) { + mockPathwrap.On("EvalSymlinks", "/Users/josh/.config/neovim").Return("/Users/josh/.config/neovim", nil) + mockGit.On("ShowTopLevel", "/Users/josh/.config/neovim").Return(false, "", fmt.Errorf("not a git repository (or any of the parent")) + mockGit.On("GitCommonDir", "/Users/josh/.config/neovim").Return(false, "", fmt.Errorf("not a git repository (or any of the parent")) + mockPathwrap.On("Base", "/Users/josh/.config/neovim").Return("neovim") + name, _ := n.Name("/Users/josh/.config/neovim") + assert.Equal(t, "neovim", name) + }) }) - t.Run("returns base on non-git dir", func(t *testing.T) { - mockGit.On("ShowTopLevel", "/Users/josh/.config/neovim").Return(false, "", fmt.Errorf("not a git repository (or any of the parent")) - mockGit.On("GitCommonDir", "/Users/josh/.config/neovim").Return(false, "", fmt.Errorf("not a git repository (or any of the parent")) - mockPathwrap.On("Base", "/Users/josh/.config/neovim").Return("neovim") - name, _ := n.Name("/Users/josh/.config/neovim") - assert.Equal(t, "neovim", name) + t.Run("when path contains a symlink", func(t *testing.T) { + mockPathwrap := new(pathwrap.MockPath) + mockGit := new(git.MockGit) + n := NewNamer(mockPathwrap, mockGit) + + t.Run("name for symlinked file in symlinked git repo", func(t *testing.T) { + mockPathwrap.On("EvalSymlinks", "/Users/josh/d/.c/neovim").Return("/Users/josh/dotfiles/.config/neovim", nil) + mockGit.On("ShowTopLevel", "/Users/josh/dotfiles/.config/neovim").Return(true, "/Users/josh/dotfiles", nil) + mockGit.On("GitCommonDir", "/Users/josh/dotfiles/.config/neovim").Return(true, "", nil) + mockPathwrap.On("Base", "/Users/josh/dotfiles").Return("dotfiles") + name, _ := n.Name("/Users/josh/d/.c/neovim") + assert.Equal(t, "dotfiles/_config/neovim", name) + }) + + t.Run("name for git worktree", func(t *testing.T) { + mockPathwrap.On("EvalSymlinks", "/Users/josh/p/sesh/main").Return("/Users/josh/projects/sesh/main", nil) + mockGit.On("ShowTopLevel", "/Users/josh/projects/sesh/main").Return(true, "/Users/josh/projects/sesh/main", nil) + mockGit.On("GitCommonDir", "/Users/josh/projects/sesh/main").Return(true, "/Users/josh/projects/sesh/.bare", nil) + mockPathwrap.On("Base", "/Users/josh/projects/sesh").Return("sesh") + name, _ := n.Name("/Users/josh/p/sesh/main") + assert.Equal(t, "sesh/main", name) + }) + + t.Run("returns base on non-git dir", func(t *testing.T) { + mockPathwrap.On("EvalSymlinks", "/Users/josh/c/neovim").Return("/Users/josh/.config/neovim", nil) + mockGit.On("ShowTopLevel", "/Users/josh/.config/neovim").Return(false, "", fmt.Errorf("not a git repository (or any of the parent")) + mockGit.On("GitCommonDir", "/Users/josh/.config/neovim").Return(false, "", fmt.Errorf("not a git repository (or any of the parent")) + mockPathwrap.On("Base", "/Users/josh/.config/neovim").Return("neovim") + name, _ := n.Name("/Users/josh/c/neovim") + assert.Equal(t, "neovim", name) + }) }) } diff --git a/pathwrap/mock_Path.go b/pathwrap/mock_Path.go index da01ea2..cdd70ad 100644 --- a/pathwrap/mock_Path.go +++ b/pathwrap/mock_Path.go @@ -119,6 +119,62 @@ func (_c *MockPath_Base_Call) RunAndReturn(run func(string) string) *MockPath_Ba return _c } +// EvalSymlinks provides a mock function with given fields: path +func (_m *MockPath) EvalSymlinks(path string) (string, error) { + ret := _m.Called(path) + + if len(ret) == 0 { + panic("no return value specified for EvalSymlinks") + } + + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(string) (string, error)); ok { + return rf(path) + } + if rf, ok := ret.Get(0).(func(string) string); ok { + r0 = rf(path) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(path) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockPath_EvalSymlinks_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EvalSymlinks' +type MockPath_EvalSymlinks_Call struct { + *mock.Call +} + +// EvalSymlinks is a helper method to define mock.On call +// - path string +func (_e *MockPath_Expecter) EvalSymlinks(path interface{}) *MockPath_EvalSymlinks_Call { + return &MockPath_EvalSymlinks_Call{Call: _e.mock.On("EvalSymlinks", path)} +} + +func (_c *MockPath_EvalSymlinks_Call) Run(run func(path string)) *MockPath_EvalSymlinks_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *MockPath_EvalSymlinks_Call) Return(_a0 string, _a1 error) *MockPath_EvalSymlinks_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockPath_EvalSymlinks_Call) RunAndReturn(run func(string) (string, error)) *MockPath_EvalSymlinks_Call { + _c.Call.Return(run) + return _c +} + // Join provides a mock function with given fields: elem func (_m *MockPath) Join(elem ...string) string { _va := make([]interface{}, len(elem)) diff --git a/pathwrap/pathwrap.go b/pathwrap/pathwrap.go index 8f9dd78..1f24182 100644 --- a/pathwrap/pathwrap.go +++ b/pathwrap/pathwrap.go @@ -9,6 +9,7 @@ type Path interface { Join(elem ...string) string Abs(path string) (string, error) Base(path string) string + EvalSymlinks(path string) (string, error) } type RealPath struct{} @@ -28,3 +29,7 @@ func (p *RealPath) Abs(path string) (string, error) { func (p *RealPath) Base(path string) string { return filepath.Base(path) } + +func (p *RealPath) EvalSymlinks(path string) (string, error) { + return filepath.EvalSymlinks(path) +}