Skip to content

Commit 0c34dd2

Browse files
#1151: Fix error with InterfaceDirRelative for external paths (#1157)
* #1151: Fix error with InterfaceDirRelative for external paths --------- Co-authored-by: LandonTClipp <11232769+LandonTClipp@users.noreply.github.com>
1 parent 0eac2ca commit 0c34dd2

8 files changed

Lines changed: 206 additions & 15 deletions

File tree

.mockery_testify.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,4 +151,8 @@ packages:
151151
# The second option asserts that the value itself is sane. https://github.com/vektra/mockery/issues/1133#issuecomment-3758283921
152152
# This mock will be explicitly tested against so that we can assert in tests that the value
153153
# does not encounter breaking changes.
154-
- dir: '{{.InterfaceDir}}/{{.InterfaceDirRelative}}'
154+
- dir: '{{.InterfaceDir}}/{{.InterfaceDirRelative}}'
155+
github.com/LandonTClipp/example-go-repo:
156+
config:
157+
all: True
158+
recursive: True

config/config.go

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"github.com/rs/zerolog"
3131
"github.com/spf13/pflag"
3232
internalConfig "github.com/vektra/mockery/v3/internal/config"
33+
internalFile "github.com/vektra/mockery/v3/internal/file"
3334
"github.com/vektra/mockery/v3/internal/logging"
3435
"github.com/vektra/mockery/v3/internal/stackerr"
3536
"github.com/vektra/mockery/v3/template_funcs"
@@ -775,24 +776,39 @@ func (c *Config) buildTemplateData(
775776
interfaceDirPath := filepath.ToSlash(filepath.Dir(ifaceFilePath))
776777

777778
var interfaceDirRelative string
778-
interfaceDirRelativePath, err := filepath.Rel(filepath.FromSlash(workingDir), filepath.FromSlash(interfaceDirPath))
779+
outsideCurrentRepo, err := isPathOutsideCurrentGoModRepo(workingDir, interfaceDirPath)
779780
if err != nil {
780781
log.Debug().
781782
Err(err).
782-
Str("working-dir", workingDir).
783-
Str("interfaceDirPath", interfaceDirPath).
784-
Str("interface-dir-relative-path", interfaceDirRelativePath).
785-
Str("iface-file-path", ifaceFilePath).
786-
Msg("can't make path relative to working dir, setting to './'")
787-
interfaceDirRelative = "."
788-
} else {
789-
interfaceDirRelativePath = filepath.ToSlash(interfaceDirRelativePath)
783+
Msg("failed to determine whether interface path is outside current go.mod repo")
784+
}
785+
if outsideCurrentRepo {
790786
log.Debug().
791787
Str("working-dir", workingDir).
792788
Str("interfaceDirPath", interfaceDirPath).
793-
Str("interface-dir-relative-path", interfaceDirRelativePath).
794-
Msg("found relative path")
795-
interfaceDirRelative = interfaceDirRelativePath
789+
Str("src-package-path", srcPkg.Types.Path()).
790+
Msg("interface path is outside current go.mod repo; using fully-qualified package path")
791+
interfaceDirRelative = srcPkg.Types.Path()
792+
} else {
793+
interfaceDirRelativePath, err := filepath.Rel(filepath.FromSlash(workingDir), filepath.FromSlash(interfaceDirPath))
794+
if err != nil {
795+
log.Debug().
796+
Err(err).
797+
Str("working-dir", workingDir).
798+
Str("interfaceDirPath", interfaceDirPath).
799+
Str("interface-dir-relative-path", interfaceDirRelativePath).
800+
Str("iface-file-path", ifaceFilePath).
801+
Msg("can't make path relative to working dir, setting to './'")
802+
interfaceDirRelative = "."
803+
} else {
804+
interfaceDirRelativePath = filepath.ToSlash(interfaceDirRelativePath)
805+
log.Debug().
806+
Str("working-dir", workingDir).
807+
Str("interfaceDirPath", interfaceDirPath).
808+
Str("interface-dir-relative-path", interfaceDirRelativePath).
809+
Msg("found relative path")
810+
interfaceDirRelative = interfaceDirRelativePath
811+
}
796812
}
797813

798814
return TemplateData{
@@ -809,6 +825,27 @@ func (c *Config) buildTemplateData(
809825
}, nil
810826
}
811827

828+
func isPathOutsideCurrentGoModRepo(workingDir string, targetPath string) (bool, error) {
829+
goModFile, _, err := internalFile.FindInHierarchy(workingDir, []string{"go.mod"})
830+
if err != nil {
831+
return false, err
832+
}
833+
834+
repoRoot := filepath.ToSlash(filepath.Dir(goModFile))
835+
targetPath = filepath.ToSlash(filepath.Clean(targetPath))
836+
837+
relFromRepoRoot, err := filepath.Rel(filepath.FromSlash(repoRoot), filepath.FromSlash(targetPath))
838+
// Windows can return an error here if the paths are on different drives.
839+
// In that case, we can just assume the target path is outside the repo.
840+
if err != nil {
841+
//nolint:nilerr // Intentional: treat cross-drive Rel errors as "outside repo", not a fatal error.
842+
return true, nil
843+
}
844+
relFromRepoRoot = filepath.ToSlash(relFromRepoRoot)
845+
846+
return relFromRepoRoot == ".." || strings.HasPrefix(relFromRepoRoot, "../"), nil
847+
}
848+
812849
func (c *Config) GetReplacement(pkgPath string, typeName string) *ReplaceType {
813850
pkgMap := c.ReplaceType[pkgPath]
814851
if pkgMap == nil {

config/config_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,3 +243,50 @@ func TestBuildTemplateData(t *testing.T) {
243243
assert.Equal(t, "github.com/test/pkg", data.SrcPackagePath)
244244
assert.Equal(t, "my-template", data.Template)
245245
}
246+
247+
func TestBuildTemplateData_InterfaceDirRelativeUsesPackagePathOutsideRepo(t *testing.T) {
248+
ctx := context.Background()
249+
250+
configFile := "/config/config.yaml"
251+
structName := "MyStruct"
252+
templateName := "my-template"
253+
254+
c := &Config{
255+
ConfigFile: &configFile,
256+
StructName: &structName,
257+
Template: &templateName,
258+
}
259+
260+
pkg := &packages.Package{
261+
Types: types.NewPackage("github.com/test/module/subpkg", "mypkg"),
262+
}
263+
264+
ifacePath := filepath.ToSlash(filepath.Join(os.TempDir(), "mockery-external-module", "foo", "bar.go"))
265+
expectedInterfaceDir := filepath.ToSlash(filepath.Dir(ifacePath))
266+
267+
data, err := c.buildTemplateData(
268+
ctx,
269+
ifacePath,
270+
"API",
271+
pkg,
272+
)
273+
require.NoError(t, err)
274+
275+
assert.Equal(t, expectedInterfaceDir, data.InterfaceDir)
276+
assert.Equal(t, "github.com/test/module/subpkg", data.InterfaceDirRelative)
277+
}
278+
279+
func TestIsPathOutsideCurrentGoModRepo(t *testing.T) {
280+
wd, err := os.Getwd()
281+
require.NoError(t, err)
282+
283+
outsidePath := filepath.ToSlash(filepath.Join(os.TempDir(), "mockery-outside-repo", "foo"))
284+
outsideRepo, err := isPathOutsideCurrentGoModRepo(wd, outsidePath)
285+
require.NoError(t, err)
286+
assert.True(t, outsideRepo)
287+
288+
insidePath := filepath.ToSlash(filepath.Join(wd, "config"))
289+
insideRepo, err := isPathOutsideCurrentGoModRepo(wd, insidePath)
290+
require.NoError(t, err)
291+
assert.False(t, insideRepo)
292+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package foo
2+
3+
import "testing"
4+
5+
func TestFoo(t *testing.T) {
6+
m := NewMockFoo(t)
7+
m.EXPECT().Bar("hello").Return(42)
8+
if got := m.Bar("hello"); got != 42 {
9+
t.Errorf("Bar() = %v, want %v", got, 42)
10+
}
11+
}

github.com/LandonTClipp/example-go-repo/mocks_testify_foo_test.go

Lines changed: 89 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

go.mod

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
module github.com/vektra/mockery/v3
22

3-
go 1.25.0
3+
go 1.25.5
44

55
require (
6+
github.com/LandonTClipp/example-go-repo v0.1.2
67
github.com/brunoga/deep v1.3.1
78
github.com/go-viper/mapstructure/v2 v2.5.0
89
github.com/huandu/xstrings v1.5.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
github.com/LandonTClipp/example-go-repo v0.1.2 h1:R4kP4EcCPuRy4l8iV8Bt1fZwLfbG7kZv7FEn+c7HxME=
2+
github.com/LandonTClipp/example-go-repo v0.1.2/go.mod h1:aKxggDcybo4AWkfCrBXcgBGqAxP+G+KT4OULG0bTQvQ=
13
github.com/brunoga/deep v1.3.1 h1:bSrL6FhAZa6JlVv4vsi7Hg8SLwroDb1kgDERRVipBCo=
24
github.com/brunoga/deep v1.3.1/go.mod h1:GDV6dnXqn80ezsLSZ5Wlv1PdKAWAO4L5PnKYtv2dgaI=
35
github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY=

go.work

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
go 1.25.0
1+
go 1.25.5
22

33
use (
44
.

0 commit comments

Comments
 (0)