Provisioning: Fix issue with double prefix for github repository (#103781)
This commit is contained in:
@@ -92,7 +92,7 @@ func IncrementalSync(ctx context.Context, repo repository.Versioned, previousRef
|
|||||||
result.Resource = gvk.Kind
|
result.Resource = gvk.Kind
|
||||||
result.Group = gvk.Group
|
result.Group = gvk.Group
|
||||||
case repository.FileActionRenamed:
|
case repository.FileActionRenamed:
|
||||||
name, gvk, err := repositoryResources.RenameResourceFile(ctx, change.Path, change.PreviousRef, change.Path, change.Ref)
|
name, gvk, err := repositoryResources.RenameResourceFile(ctx, change.PreviousPath, change.PreviousRef, change.Path, change.Ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
result.Error = err
|
result.Error = err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -220,7 +220,7 @@ func TestIncrementalSync(t *testing.T) {
|
|||||||
progress.On("SetMessage", mock.Anything, "versioned changes replicated").Return()
|
progress.On("SetMessage", mock.Anything, "versioned changes replicated").Return()
|
||||||
|
|
||||||
// Mock resource rename
|
// Mock resource rename
|
||||||
repoResources.On("RenameResourceFile", mock.Anything, "dashboards/new.json", "old-ref", "dashboards/new.json", "new-ref").
|
repoResources.On("RenameResourceFile", mock.Anything, "dashboards/old.json", "old-ref", "dashboards/new.json", "new-ref").
|
||||||
Return("renamed-dashboard", schema.GroupVersionKind{Kind: "Dashboard", Group: "dashboards"}, nil)
|
Return("renamed-dashboard", schema.GroupVersionKind{Kind: "Dashboard", Group: "dashboards"}, nil)
|
||||||
|
|
||||||
// Mock progress recording
|
// Mock progress recording
|
||||||
|
|||||||
@@ -262,8 +262,15 @@ func (r *githubRepository) ReadTree(ctx context.Context, ref string) ([]FileTree
|
|||||||
|
|
||||||
entries := make([]FileTreeEntry, 0, len(tree))
|
entries := make([]FileTreeEntry, 0, len(tree))
|
||||||
for _, entry := range tree {
|
for _, entry := range tree {
|
||||||
|
isBlob := !entry.IsDirectory()
|
||||||
|
// FIXME: this we could potentially do somewhere else on in a different way
|
||||||
|
filePath := entry.GetPath()
|
||||||
|
if !isBlob && !safepath.IsDir(filePath) {
|
||||||
|
filePath = filePath + "/"
|
||||||
|
}
|
||||||
|
|
||||||
converted := FileTreeEntry{
|
converted := FileTreeEntry{
|
||||||
Path: entry.GetPath(),
|
Path: filePath,
|
||||||
Size: entry.GetSize(),
|
Size: entry.GetSize(),
|
||||||
Hash: entry.GetSHA(),
|
Hash: entry.GetSHA(),
|
||||||
Blob: !entry.IsDirectory(),
|
Blob: !entry.IsDirectory(),
|
||||||
@@ -672,30 +679,75 @@ func (r *githubRepository) CompareFiles(ctx context.Context, base, ref string) (
|
|||||||
// reference: https://docs.github.com/en/rest/commits/commits?apiVersion=2022-11-28#get-a-commit
|
// reference: https://docs.github.com/en/rest/commits/commits?apiVersion=2022-11-28#get-a-commit
|
||||||
switch f.GetStatus() {
|
switch f.GetStatus() {
|
||||||
case "added", "copied":
|
case "added", "copied":
|
||||||
|
currentPath, err := safepath.RelativeTo(f.GetFilename(), r.config.Spec.GitHub.Path)
|
||||||
|
if err != nil {
|
||||||
|
// do nothing as it's outside of configured path
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
changes = append(changes, VersionedFileChange{
|
changes = append(changes, VersionedFileChange{
|
||||||
Path: f.GetFilename(),
|
Path: currentPath,
|
||||||
Ref: ref,
|
Ref: ref,
|
||||||
Action: FileActionCreated,
|
Action: FileActionCreated,
|
||||||
})
|
})
|
||||||
case "modified", "changed":
|
case "modified", "changed":
|
||||||
|
currentPath, err := safepath.RelativeTo(f.GetFilename(), r.config.Spec.GitHub.Path)
|
||||||
|
if err != nil {
|
||||||
|
// do nothing as it's outside of configured path
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
changes = append(changes, VersionedFileChange{
|
changes = append(changes, VersionedFileChange{
|
||||||
Path: f.GetFilename(),
|
Path: currentPath,
|
||||||
Ref: ref,
|
Ref: ref,
|
||||||
Action: FileActionUpdated,
|
Action: FileActionUpdated,
|
||||||
})
|
})
|
||||||
case "renamed":
|
case "renamed":
|
||||||
|
previousPath, previousErr := safepath.RelativeTo(f.GetPreviousFilename(), r.config.Spec.GitHub.Path)
|
||||||
|
currentPath, currentErr := safepath.RelativeTo(f.GetFilename(), r.config.Spec.GitHub.Path)
|
||||||
|
|
||||||
|
// Handle all possible combinations of path validation results:
|
||||||
|
// 1. Both paths outside configured path, do nothing
|
||||||
|
// 2. Both paths inside configured path, rename
|
||||||
|
// 3. Moving out of configured path, delete previous file
|
||||||
|
// 4. Moving into configured path, create new file
|
||||||
|
switch {
|
||||||
|
case previousErr != nil && currentErr != nil:
|
||||||
|
// do nothing as it's outside of configured path
|
||||||
|
case previousErr == nil && currentErr == nil:
|
||||||
|
changes = append(changes, VersionedFileChange{
|
||||||
|
Path: currentPath,
|
||||||
|
PreviousPath: previousPath,
|
||||||
|
Ref: ref,
|
||||||
|
PreviousRef: base,
|
||||||
|
Action: FileActionRenamed,
|
||||||
|
})
|
||||||
|
case previousErr == nil && currentErr != nil:
|
||||||
|
changes = append(changes, VersionedFileChange{
|
||||||
|
Path: currentPath,
|
||||||
|
Ref: ref,
|
||||||
|
Action: FileActionDeleted,
|
||||||
|
})
|
||||||
|
case previousErr != nil && currentErr == nil:
|
||||||
|
changes = append(changes, VersionedFileChange{
|
||||||
|
Path: currentPath,
|
||||||
|
Ref: ref,
|
||||||
|
Action: FileActionCreated,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
case "removed":
|
||||||
|
currentPath, err := safepath.RelativeTo(f.GetFilename(), r.config.Spec.GitHub.Path)
|
||||||
|
if err != nil {
|
||||||
|
// do nothing as it's outside of configured path
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
changes = append(changes, VersionedFileChange{
|
changes = append(changes, VersionedFileChange{
|
||||||
Path: f.GetFilename(),
|
|
||||||
PreviousPath: f.GetPreviousFilename(),
|
|
||||||
Ref: ref,
|
Ref: ref,
|
||||||
PreviousRef: base,
|
PreviousRef: base,
|
||||||
Action: FileActionRenamed,
|
Path: currentPath,
|
||||||
})
|
PreviousPath: currentPath,
|
||||||
case "removed":
|
Action: FileActionDeleted,
|
||||||
changes = append(changes, VersionedFileChange{
|
|
||||||
Ref: base,
|
|
||||||
Path: f.GetFilename(),
|
|
||||||
Action: FileActionDeleted,
|
|
||||||
})
|
})
|
||||||
case "unchanged":
|
case "unchanged":
|
||||||
// do nothing
|
// do nothing
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ const MaxFileSize = 10 * 1024 * 1024 // 10MB in bytes
|
|||||||
|
|
||||||
type ErrRateLimited = github.RateLimitError
|
type ErrRateLimited = github.RateLimitError
|
||||||
|
|
||||||
|
//go:generate mockery --name Client --structname MockClient --inpackage --filename mock_client.go --with-expecter
|
||||||
type Client interface {
|
type Client interface {
|
||||||
// IsAuthenticated checks if the client is authenticated.
|
// IsAuthenticated checks if the client is authenticated.
|
||||||
IsAuthenticated(ctx context.Context) error
|
IsAuthenticated(ctx context.Context) error
|
||||||
@@ -103,6 +104,7 @@ type Client interface {
|
|||||||
ClearAllPullRequestFileComments(ctx context.Context, owner, repository string, number int) error
|
ClearAllPullRequestFileComments(ctx context.Context, owner, repository string, number int) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:generate mockery --name RepositoryContent --structname MockRepositoryContent --inpackage --filename mock_repository_content.go --with-expecter
|
||||||
type RepositoryContent interface {
|
type RepositoryContent interface {
|
||||||
// Returns true if this is a directory, false if it is a file.
|
// Returns true if this is a directory, false if it is a file.
|
||||||
IsDirectory() bool
|
IsDirectory() bool
|
||||||
@@ -142,6 +144,7 @@ type Commit struct {
|
|||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:generate mockery --name CommitFile --structname MockCommitFile --inpackage --filename mock_commit_file.go --with-expecter
|
||||||
type CommitFile interface {
|
type CommitFile interface {
|
||||||
GetSHA() string
|
GetSHA() string
|
||||||
GetFilename() string
|
GetFilename() string
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,212 @@
|
|||||||
|
// Code generated by mockery v2.52.4. DO NOT EDIT.
|
||||||
|
|
||||||
|
package github
|
||||||
|
|
||||||
|
import mock "github.com/stretchr/testify/mock"
|
||||||
|
|
||||||
|
// MockCommitFile is an autogenerated mock type for the CommitFile type
|
||||||
|
type MockCommitFile struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
type MockCommitFile_Expecter struct {
|
||||||
|
mock *mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_m *MockCommitFile) EXPECT() *MockCommitFile_Expecter {
|
||||||
|
return &MockCommitFile_Expecter{mock: &_m.Mock}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFilename provides a mock function with no fields
|
||||||
|
func (_m *MockCommitFile) GetFilename() string {
|
||||||
|
ret := _m.Called()
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for GetFilename")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 string
|
||||||
|
if rf, ok := ret.Get(0).(func() string); ok {
|
||||||
|
r0 = rf()
|
||||||
|
} else {
|
||||||
|
r0 = ret.Get(0).(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockCommitFile_GetFilename_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetFilename'
|
||||||
|
type MockCommitFile_GetFilename_Call struct {
|
||||||
|
*mock.Call
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFilename is a helper method to define mock.On call
|
||||||
|
func (_e *MockCommitFile_Expecter) GetFilename() *MockCommitFile_GetFilename_Call {
|
||||||
|
return &MockCommitFile_GetFilename_Call{Call: _e.mock.On("GetFilename")}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockCommitFile_GetFilename_Call) Run(run func()) *MockCommitFile_GetFilename_Call {
|
||||||
|
_c.Call.Run(func(args mock.Arguments) {
|
||||||
|
run()
|
||||||
|
})
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockCommitFile_GetFilename_Call) Return(_a0 string) *MockCommitFile_GetFilename_Call {
|
||||||
|
_c.Call.Return(_a0)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockCommitFile_GetFilename_Call) RunAndReturn(run func() string) *MockCommitFile_GetFilename_Call {
|
||||||
|
_c.Call.Return(run)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPreviousFilename provides a mock function with no fields
|
||||||
|
func (_m *MockCommitFile) GetPreviousFilename() string {
|
||||||
|
ret := _m.Called()
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for GetPreviousFilename")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 string
|
||||||
|
if rf, ok := ret.Get(0).(func() string); ok {
|
||||||
|
r0 = rf()
|
||||||
|
} else {
|
||||||
|
r0 = ret.Get(0).(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockCommitFile_GetPreviousFilename_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPreviousFilename'
|
||||||
|
type MockCommitFile_GetPreviousFilename_Call struct {
|
||||||
|
*mock.Call
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPreviousFilename is a helper method to define mock.On call
|
||||||
|
func (_e *MockCommitFile_Expecter) GetPreviousFilename() *MockCommitFile_GetPreviousFilename_Call {
|
||||||
|
return &MockCommitFile_GetPreviousFilename_Call{Call: _e.mock.On("GetPreviousFilename")}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockCommitFile_GetPreviousFilename_Call) Run(run func()) *MockCommitFile_GetPreviousFilename_Call {
|
||||||
|
_c.Call.Run(func(args mock.Arguments) {
|
||||||
|
run()
|
||||||
|
})
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockCommitFile_GetPreviousFilename_Call) Return(_a0 string) *MockCommitFile_GetPreviousFilename_Call {
|
||||||
|
_c.Call.Return(_a0)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockCommitFile_GetPreviousFilename_Call) RunAndReturn(run func() string) *MockCommitFile_GetPreviousFilename_Call {
|
||||||
|
_c.Call.Return(run)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSHA provides a mock function with no fields
|
||||||
|
func (_m *MockCommitFile) GetSHA() string {
|
||||||
|
ret := _m.Called()
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for GetSHA")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 string
|
||||||
|
if rf, ok := ret.Get(0).(func() string); ok {
|
||||||
|
r0 = rf()
|
||||||
|
} else {
|
||||||
|
r0 = ret.Get(0).(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockCommitFile_GetSHA_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetSHA'
|
||||||
|
type MockCommitFile_GetSHA_Call struct {
|
||||||
|
*mock.Call
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSHA is a helper method to define mock.On call
|
||||||
|
func (_e *MockCommitFile_Expecter) GetSHA() *MockCommitFile_GetSHA_Call {
|
||||||
|
return &MockCommitFile_GetSHA_Call{Call: _e.mock.On("GetSHA")}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockCommitFile_GetSHA_Call) Run(run func()) *MockCommitFile_GetSHA_Call {
|
||||||
|
_c.Call.Run(func(args mock.Arguments) {
|
||||||
|
run()
|
||||||
|
})
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockCommitFile_GetSHA_Call) Return(_a0 string) *MockCommitFile_GetSHA_Call {
|
||||||
|
_c.Call.Return(_a0)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockCommitFile_GetSHA_Call) RunAndReturn(run func() string) *MockCommitFile_GetSHA_Call {
|
||||||
|
_c.Call.Return(run)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStatus provides a mock function with no fields
|
||||||
|
func (_m *MockCommitFile) GetStatus() string {
|
||||||
|
ret := _m.Called()
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for GetStatus")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 string
|
||||||
|
if rf, ok := ret.Get(0).(func() string); ok {
|
||||||
|
r0 = rf()
|
||||||
|
} else {
|
||||||
|
r0 = ret.Get(0).(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockCommitFile_GetStatus_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetStatus'
|
||||||
|
type MockCommitFile_GetStatus_Call struct {
|
||||||
|
*mock.Call
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStatus is a helper method to define mock.On call
|
||||||
|
func (_e *MockCommitFile_Expecter) GetStatus() *MockCommitFile_GetStatus_Call {
|
||||||
|
return &MockCommitFile_GetStatus_Call{Call: _e.mock.On("GetStatus")}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockCommitFile_GetStatus_Call) Run(run func()) *MockCommitFile_GetStatus_Call {
|
||||||
|
_c.Call.Run(func(args mock.Arguments) {
|
||||||
|
run()
|
||||||
|
})
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockCommitFile_GetStatus_Call) Return(_a0 string) *MockCommitFile_GetStatus_Call {
|
||||||
|
_c.Call.Return(_a0)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockCommitFile_GetStatus_Call) RunAndReturn(run func() string) *MockCommitFile_GetStatus_Call {
|
||||||
|
_c.Call.Return(run)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockCommitFile creates a new instance of MockCommitFile. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||||
|
// The first argument is typically a *testing.T value.
|
||||||
|
func NewMockCommitFile(t interface {
|
||||||
|
mock.TestingT
|
||||||
|
Cleanup(func())
|
||||||
|
}) *MockCommitFile {
|
||||||
|
mock := &MockCommitFile{}
|
||||||
|
mock.Mock.Test(t)
|
||||||
|
|
||||||
|
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||||
|
|
||||||
|
return mock
|
||||||
|
}
|
||||||
@@ -0,0 +1,312 @@
|
|||||||
|
// Code generated by mockery v2.52.4. DO NOT EDIT.
|
||||||
|
|
||||||
|
package github
|
||||||
|
|
||||||
|
import mock "github.com/stretchr/testify/mock"
|
||||||
|
|
||||||
|
// MockRepositoryContent is an autogenerated mock type for the RepositoryContent type
|
||||||
|
type MockRepositoryContent struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
type MockRepositoryContent_Expecter struct {
|
||||||
|
mock *mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_m *MockRepositoryContent) EXPECT() *MockRepositoryContent_Expecter {
|
||||||
|
return &MockRepositoryContent_Expecter{mock: &_m.Mock}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFileContent provides a mock function with no fields
|
||||||
|
func (_m *MockRepositoryContent) GetFileContent() (string, error) {
|
||||||
|
ret := _m.Called()
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for GetFileContent")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 string
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(0).(func() (string, error)); ok {
|
||||||
|
return rf()
|
||||||
|
}
|
||||||
|
if rf, ok := ret.Get(0).(func() string); ok {
|
||||||
|
r0 = rf()
|
||||||
|
} else {
|
||||||
|
r0 = ret.Get(0).(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rf, ok := ret.Get(1).(func() error); ok {
|
||||||
|
r1 = rf()
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockRepositoryContent_GetFileContent_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetFileContent'
|
||||||
|
type MockRepositoryContent_GetFileContent_Call struct {
|
||||||
|
*mock.Call
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFileContent is a helper method to define mock.On call
|
||||||
|
func (_e *MockRepositoryContent_Expecter) GetFileContent() *MockRepositoryContent_GetFileContent_Call {
|
||||||
|
return &MockRepositoryContent_GetFileContent_Call{Call: _e.mock.On("GetFileContent")}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockRepositoryContent_GetFileContent_Call) Run(run func()) *MockRepositoryContent_GetFileContent_Call {
|
||||||
|
_c.Call.Run(func(args mock.Arguments) {
|
||||||
|
run()
|
||||||
|
})
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockRepositoryContent_GetFileContent_Call) Return(_a0 string, _a1 error) *MockRepositoryContent_GetFileContent_Call {
|
||||||
|
_c.Call.Return(_a0, _a1)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockRepositoryContent_GetFileContent_Call) RunAndReturn(run func() (string, error)) *MockRepositoryContent_GetFileContent_Call {
|
||||||
|
_c.Call.Return(run)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPath provides a mock function with no fields
|
||||||
|
func (_m *MockRepositoryContent) GetPath() string {
|
||||||
|
ret := _m.Called()
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for GetPath")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 string
|
||||||
|
if rf, ok := ret.Get(0).(func() string); ok {
|
||||||
|
r0 = rf()
|
||||||
|
} else {
|
||||||
|
r0 = ret.Get(0).(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockRepositoryContent_GetPath_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPath'
|
||||||
|
type MockRepositoryContent_GetPath_Call struct {
|
||||||
|
*mock.Call
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPath is a helper method to define mock.On call
|
||||||
|
func (_e *MockRepositoryContent_Expecter) GetPath() *MockRepositoryContent_GetPath_Call {
|
||||||
|
return &MockRepositoryContent_GetPath_Call{Call: _e.mock.On("GetPath")}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockRepositoryContent_GetPath_Call) Run(run func()) *MockRepositoryContent_GetPath_Call {
|
||||||
|
_c.Call.Run(func(args mock.Arguments) {
|
||||||
|
run()
|
||||||
|
})
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockRepositoryContent_GetPath_Call) Return(_a0 string) *MockRepositoryContent_GetPath_Call {
|
||||||
|
_c.Call.Return(_a0)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockRepositoryContent_GetPath_Call) RunAndReturn(run func() string) *MockRepositoryContent_GetPath_Call {
|
||||||
|
_c.Call.Return(run)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSHA provides a mock function with no fields
|
||||||
|
func (_m *MockRepositoryContent) GetSHA() string {
|
||||||
|
ret := _m.Called()
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for GetSHA")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 string
|
||||||
|
if rf, ok := ret.Get(0).(func() string); ok {
|
||||||
|
r0 = rf()
|
||||||
|
} else {
|
||||||
|
r0 = ret.Get(0).(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockRepositoryContent_GetSHA_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetSHA'
|
||||||
|
type MockRepositoryContent_GetSHA_Call struct {
|
||||||
|
*mock.Call
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSHA is a helper method to define mock.On call
|
||||||
|
func (_e *MockRepositoryContent_Expecter) GetSHA() *MockRepositoryContent_GetSHA_Call {
|
||||||
|
return &MockRepositoryContent_GetSHA_Call{Call: _e.mock.On("GetSHA")}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockRepositoryContent_GetSHA_Call) Run(run func()) *MockRepositoryContent_GetSHA_Call {
|
||||||
|
_c.Call.Run(func(args mock.Arguments) {
|
||||||
|
run()
|
||||||
|
})
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockRepositoryContent_GetSHA_Call) Return(_a0 string) *MockRepositoryContent_GetSHA_Call {
|
||||||
|
_c.Call.Return(_a0)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockRepositoryContent_GetSHA_Call) RunAndReturn(run func() string) *MockRepositoryContent_GetSHA_Call {
|
||||||
|
_c.Call.Return(run)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSize provides a mock function with no fields
|
||||||
|
func (_m *MockRepositoryContent) GetSize() int64 {
|
||||||
|
ret := _m.Called()
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for GetSize")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 int64
|
||||||
|
if rf, ok := ret.Get(0).(func() int64); ok {
|
||||||
|
r0 = rf()
|
||||||
|
} else {
|
||||||
|
r0 = ret.Get(0).(int64)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockRepositoryContent_GetSize_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetSize'
|
||||||
|
type MockRepositoryContent_GetSize_Call struct {
|
||||||
|
*mock.Call
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSize is a helper method to define mock.On call
|
||||||
|
func (_e *MockRepositoryContent_Expecter) GetSize() *MockRepositoryContent_GetSize_Call {
|
||||||
|
return &MockRepositoryContent_GetSize_Call{Call: _e.mock.On("GetSize")}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockRepositoryContent_GetSize_Call) Run(run func()) *MockRepositoryContent_GetSize_Call {
|
||||||
|
_c.Call.Run(func(args mock.Arguments) {
|
||||||
|
run()
|
||||||
|
})
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockRepositoryContent_GetSize_Call) Return(_a0 int64) *MockRepositoryContent_GetSize_Call {
|
||||||
|
_c.Call.Return(_a0)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockRepositoryContent_GetSize_Call) RunAndReturn(run func() int64) *MockRepositoryContent_GetSize_Call {
|
||||||
|
_c.Call.Return(run)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsDirectory provides a mock function with no fields
|
||||||
|
func (_m *MockRepositoryContent) IsDirectory() bool {
|
||||||
|
ret := _m.Called()
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for IsDirectory")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 bool
|
||||||
|
if rf, ok := ret.Get(0).(func() bool); ok {
|
||||||
|
r0 = rf()
|
||||||
|
} else {
|
||||||
|
r0 = ret.Get(0).(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockRepositoryContent_IsDirectory_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsDirectory'
|
||||||
|
type MockRepositoryContent_IsDirectory_Call struct {
|
||||||
|
*mock.Call
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsDirectory is a helper method to define mock.On call
|
||||||
|
func (_e *MockRepositoryContent_Expecter) IsDirectory() *MockRepositoryContent_IsDirectory_Call {
|
||||||
|
return &MockRepositoryContent_IsDirectory_Call{Call: _e.mock.On("IsDirectory")}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockRepositoryContent_IsDirectory_Call) Run(run func()) *MockRepositoryContent_IsDirectory_Call {
|
||||||
|
_c.Call.Run(func(args mock.Arguments) {
|
||||||
|
run()
|
||||||
|
})
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockRepositoryContent_IsDirectory_Call) Return(_a0 bool) *MockRepositoryContent_IsDirectory_Call {
|
||||||
|
_c.Call.Return(_a0)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockRepositoryContent_IsDirectory_Call) RunAndReturn(run func() bool) *MockRepositoryContent_IsDirectory_Call {
|
||||||
|
_c.Call.Return(run)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSymlink provides a mock function with no fields
|
||||||
|
func (_m *MockRepositoryContent) IsSymlink() bool {
|
||||||
|
ret := _m.Called()
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for IsSymlink")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 bool
|
||||||
|
if rf, ok := ret.Get(0).(func() bool); ok {
|
||||||
|
r0 = rf()
|
||||||
|
} else {
|
||||||
|
r0 = ret.Get(0).(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockRepositoryContent_IsSymlink_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsSymlink'
|
||||||
|
type MockRepositoryContent_IsSymlink_Call struct {
|
||||||
|
*mock.Call
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSymlink is a helper method to define mock.On call
|
||||||
|
func (_e *MockRepositoryContent_Expecter) IsSymlink() *MockRepositoryContent_IsSymlink_Call {
|
||||||
|
return &MockRepositoryContent_IsSymlink_Call{Call: _e.mock.On("IsSymlink")}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockRepositoryContent_IsSymlink_Call) Run(run func()) *MockRepositoryContent_IsSymlink_Call {
|
||||||
|
_c.Call.Run(func(args mock.Arguments) {
|
||||||
|
run()
|
||||||
|
})
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockRepositoryContent_IsSymlink_Call) Return(_a0 bool) *MockRepositoryContent_IsSymlink_Call {
|
||||||
|
_c.Call.Return(_a0)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockRepositoryContent_IsSymlink_Call) RunAndReturn(run func() bool) *MockRepositoryContent_IsSymlink_Call {
|
||||||
|
_c.Call.Return(run)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockRepositoryContent creates a new instance of MockRepositoryContent. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||||
|
// The first argument is typically a *testing.T value.
|
||||||
|
func NewMockRepositoryContent(t interface {
|
||||||
|
mock.TestingT
|
||||||
|
Cleanup(func())
|
||||||
|
}) *MockRepositoryContent {
|
||||||
|
mock := &MockRepositoryContent{}
|
||||||
|
mock.Mock.Test(t)
|
||||||
|
|
||||||
|
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||||
|
|
||||||
|
return mock
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package repository
|
package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
context "context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@@ -8,10 +9,12 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
mock "github.com/stretchr/testify/mock"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1"
|
provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1"
|
||||||
|
pgh "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository/github"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIsValidGitBranchName(t *testing.T) {
|
func TestIsValidGitBranchName(t *testing.T) {
|
||||||
@@ -147,3 +150,101 @@ func TestParseWebhooks(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestReadTree(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
path string
|
||||||
|
tree []pgh.RepositoryContent
|
||||||
|
expected []FileTreeEntry
|
||||||
|
}{
|
||||||
|
{name: "empty tree", tree: []pgh.RepositoryContent{}, expected: []FileTreeEntry{}},
|
||||||
|
{name: "single file", tree: func() []pgh.RepositoryContent {
|
||||||
|
content := pgh.NewMockRepositoryContent(t)
|
||||||
|
content.EXPECT().GetPath().Return("file.txt")
|
||||||
|
content.EXPECT().GetSize().Return(int64(100))
|
||||||
|
content.EXPECT().GetSHA().Return("abc123")
|
||||||
|
content.EXPECT().IsDirectory().Return(false)
|
||||||
|
return []pgh.RepositoryContent{content}
|
||||||
|
}(), expected: []FileTreeEntry{
|
||||||
|
{Path: "file.txt", Size: 100, Hash: "abc123", Blob: true},
|
||||||
|
}},
|
||||||
|
{name: "single directory", tree: func() []pgh.RepositoryContent {
|
||||||
|
content := pgh.NewMockRepositoryContent(t)
|
||||||
|
content.EXPECT().GetPath().Return("dir")
|
||||||
|
content.EXPECT().IsDirectory().Return(true)
|
||||||
|
content.EXPECT().GetSize().Return(int64(0))
|
||||||
|
content.EXPECT().GetSHA().Return("")
|
||||||
|
|
||||||
|
return []pgh.RepositoryContent{content}
|
||||||
|
}(), expected: []FileTreeEntry{
|
||||||
|
{Path: "dir/", Blob: false},
|
||||||
|
}},
|
||||||
|
{name: "mixed content", tree: func() []pgh.RepositoryContent {
|
||||||
|
file1 := pgh.NewMockRepositoryContent(t)
|
||||||
|
file1.EXPECT().GetPath().Return("file1.txt")
|
||||||
|
file1.EXPECT().GetSize().Return(int64(100))
|
||||||
|
file1.EXPECT().GetSHA().Return("abc123")
|
||||||
|
file1.EXPECT().IsDirectory().Return(false)
|
||||||
|
|
||||||
|
dir := pgh.NewMockRepositoryContent(t)
|
||||||
|
dir.EXPECT().GetPath().Return("dir")
|
||||||
|
dir.EXPECT().IsDirectory().Return(true)
|
||||||
|
dir.EXPECT().GetSize().Return(int64(0))
|
||||||
|
dir.EXPECT().GetSHA().Return("")
|
||||||
|
|
||||||
|
file2 := pgh.NewMockRepositoryContent(t)
|
||||||
|
file2.EXPECT().GetPath().Return("file2.txt")
|
||||||
|
file2.EXPECT().GetSize().Return(int64(200))
|
||||||
|
file2.EXPECT().GetSHA().Return("def456")
|
||||||
|
file2.EXPECT().IsDirectory().Return(false)
|
||||||
|
|
||||||
|
return []pgh.RepositoryContent{file1, dir, file2}
|
||||||
|
}(), expected: []FileTreeEntry{
|
||||||
|
{Path: "file1.txt", Size: 100, Hash: "abc123", Blob: true},
|
||||||
|
{Path: "dir/", Blob: false},
|
||||||
|
{Path: "file2.txt", Size: 200, Hash: "def456", Blob: true},
|
||||||
|
}},
|
||||||
|
{name: "with path prefix", path: "prefix", tree: func() []pgh.RepositoryContent {
|
||||||
|
file := pgh.NewMockRepositoryContent(t)
|
||||||
|
file.EXPECT().GetPath().Return("file.txt")
|
||||||
|
file.EXPECT().GetSize().Return(int64(100))
|
||||||
|
file.EXPECT().GetSHA().Return("abc123")
|
||||||
|
file.EXPECT().IsDirectory().Return(false)
|
||||||
|
|
||||||
|
dir := pgh.NewMockRepositoryContent(t)
|
||||||
|
dir.EXPECT().GetPath().Return("dir")
|
||||||
|
dir.EXPECT().GetSize().Return(int64(0))
|
||||||
|
dir.EXPECT().GetSHA().Return("")
|
||||||
|
dir.EXPECT().IsDirectory().Return(true)
|
||||||
|
|
||||||
|
return []pgh.RepositoryContent{file, dir}
|
||||||
|
}(), expected: []FileTreeEntry{
|
||||||
|
{Path: "file.txt", Size: 100, Hash: "abc123", Blob: true},
|
||||||
|
{Path: "dir/", Blob: false},
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
ghMock := pgh.NewMockClient(t)
|
||||||
|
gh := &githubRepository{
|
||||||
|
owner: "owner",
|
||||||
|
repo: "repo",
|
||||||
|
config: &provisioning.Repository{
|
||||||
|
Spec: provisioning.RepositorySpec{
|
||||||
|
GitHub: &provisioning.GitHubRepositoryConfig{
|
||||||
|
Path: tt.path,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
gh: ghMock,
|
||||||
|
}
|
||||||
|
|
||||||
|
ghMock.On("GetTree", mock.Anything, "owner", "repo", tt.path, "some-ref", true).Return(tt.tree, false, nil)
|
||||||
|
tree, err := gh.ReadTree(context.Background(), "some-ref")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tt.expected, tree)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -46,6 +46,9 @@ func TestLocalResolver(t *testing.T) {
|
|||||||
"client.go",
|
"client.go",
|
||||||
"factory.go",
|
"factory.go",
|
||||||
"impl.go",
|
"impl.go",
|
||||||
|
"mock_client.go",
|
||||||
|
"mock_commit_file.go",
|
||||||
|
"mock_repository_content.go",
|
||||||
"testdata",
|
"testdata",
|
||||||
"testdata/webhook-issue_comment-created.json",
|
"testdata/webhook-issue_comment-created.json",
|
||||||
"testdata/webhook-ping-check.json",
|
"testdata/webhook-ping-check.json",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package safepath
|
package safepath
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -36,3 +37,29 @@ func Dir(filePath string) string {
|
|||||||
func InDir(filePath, dir string) bool {
|
func InDir(filePath, dir string) bool {
|
||||||
return strings.HasPrefix(filePath, dir)
|
return strings.HasPrefix(filePath, dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RelativeTo returns the relative path of the filePath to the given directory.
|
||||||
|
// It handles cases where either filePath or dir have leading or trailing slashes.
|
||||||
|
func RelativeTo(filePath, dir string) (string, error) {
|
||||||
|
if dir == "/" || dir == "" {
|
||||||
|
return filePath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize paths by trimming leading and trailing slashes
|
||||||
|
normalizedDir := strings.Trim(dir, "/")
|
||||||
|
if normalizedDir != "" {
|
||||||
|
normalizedDir += "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
normalizedPath := strings.TrimPrefix(filePath, "/")
|
||||||
|
|
||||||
|
// Check if the normalized path is in the normalized directory
|
||||||
|
if !strings.HasPrefix(normalizedPath, normalizedDir) {
|
||||||
|
return "", fmt.Errorf("filePath is not a subdirectory of dir")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the relative path by trimming the directory prefix
|
||||||
|
relativePath := strings.TrimPrefix(normalizedPath, normalizedDir)
|
||||||
|
|
||||||
|
return relativePath, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -160,3 +160,101 @@ func TestInDir(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRelativeTo(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
filePath string
|
||||||
|
dir string
|
||||||
|
want string
|
||||||
|
expectError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "simple relative path",
|
||||||
|
filePath: "folder/subfolder/file.txt",
|
||||||
|
dir: "folder",
|
||||||
|
want: "subfolder/file.txt",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "relative path with leading slash",
|
||||||
|
filePath: "/prefix/folder/subfolder/file.txt",
|
||||||
|
dir: "/prefix/folder",
|
||||||
|
want: "subfolder/file.txt",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "relative path with leading slash in dir but in filePath",
|
||||||
|
filePath: "prefix/folder/subfolder/file.txt",
|
||||||
|
dir: "/prefix/folder",
|
||||||
|
want: "subfolder/file.txt",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with trailing slash in dir",
|
||||||
|
filePath: "folder/subfolder/file.txt",
|
||||||
|
dir: "folder/",
|
||||||
|
want: "subfolder/file.txt",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with trailing slash in both",
|
||||||
|
filePath: "folder/subfolder/",
|
||||||
|
dir: "folder/",
|
||||||
|
want: "subfolder/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty directory",
|
||||||
|
filePath: "file.txt",
|
||||||
|
dir: "",
|
||||||
|
want: "file.txt",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "directory is root",
|
||||||
|
filePath: "folder/file.txt",
|
||||||
|
dir: "/",
|
||||||
|
want: "folder/file.txt",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nested directories",
|
||||||
|
filePath: "a/b/c/d/file.txt",
|
||||||
|
dir: "a/b",
|
||||||
|
want: "c/d/file.txt",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "file not in directory",
|
||||||
|
filePath: "other/file.txt",
|
||||||
|
dir: "folder",
|
||||||
|
want: "",
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "file path shorter than directory",
|
||||||
|
filePath: "file.txt",
|
||||||
|
dir: "folder/subfolder",
|
||||||
|
want: "",
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "same directory",
|
||||||
|
filePath: "folder/file.txt",
|
||||||
|
dir: "folder",
|
||||||
|
want: "file.txt",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "directory with similar prefix",
|
||||||
|
filePath: "folder2/file.txt",
|
||||||
|
dir: "folder",
|
||||||
|
want: "",
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got, err := RelativeTo(tt.filePath, tt.dir)
|
||||||
|
if tt.expectError {
|
||||||
|
require.Error(t, err)
|
||||||
|
} else {
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tt.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user