Provisioning: Fix issue with double prefix for github repository (#103781)
This commit is contained in:
committed by
GitHub
parent
45a55234e1
commit
2eaeff8ea7
@@ -92,7 +92,7 @@ func IncrementalSync(ctx context.Context, repo repository.Versioned, previousRef
|
||||
result.Resource = gvk.Kind
|
||||
result.Group = gvk.Group
|
||||
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 {
|
||||
result.Error = err
|
||||
}
|
||||
|
||||
@@ -220,7 +220,7 @@ func TestIncrementalSync(t *testing.T) {
|
||||
progress.On("SetMessage", mock.Anything, "versioned changes replicated").Return()
|
||||
|
||||
// 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)
|
||||
|
||||
// Mock progress recording
|
||||
|
||||
@@ -262,8 +262,15 @@ func (r *githubRepository) ReadTree(ctx context.Context, ref string) ([]FileTree
|
||||
|
||||
entries := make([]FileTreeEntry, 0, len(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{
|
||||
Path: entry.GetPath(),
|
||||
Path: filePath,
|
||||
Size: entry.GetSize(),
|
||||
Hash: entry.GetSHA(),
|
||||
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
|
||||
switch f.GetStatus() {
|
||||
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{
|
||||
Path: f.GetFilename(),
|
||||
Path: currentPath,
|
||||
Ref: ref,
|
||||
Action: FileActionCreated,
|
||||
})
|
||||
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{
|
||||
Path: f.GetFilename(),
|
||||
Path: currentPath,
|
||||
Ref: ref,
|
||||
Action: FileActionUpdated,
|
||||
})
|
||||
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{
|
||||
Path: f.GetFilename(),
|
||||
PreviousPath: f.GetPreviousFilename(),
|
||||
Ref: ref,
|
||||
PreviousRef: base,
|
||||
Action: FileActionRenamed,
|
||||
})
|
||||
case "removed":
|
||||
changes = append(changes, VersionedFileChange{
|
||||
Ref: base,
|
||||
Path: f.GetFilename(),
|
||||
Action: FileActionDeleted,
|
||||
Path: currentPath,
|
||||
PreviousPath: currentPath,
|
||||
Action: FileActionDeleted,
|
||||
})
|
||||
case "unchanged":
|
||||
// do nothing
|
||||
|
||||
@@ -29,6 +29,7 @@ const MaxFileSize = 10 * 1024 * 1024 // 10MB in bytes
|
||||
|
||||
type ErrRateLimited = github.RateLimitError
|
||||
|
||||
//go:generate mockery --name Client --structname MockClient --inpackage --filename mock_client.go --with-expecter
|
||||
type Client interface {
|
||||
// IsAuthenticated checks if the client is authenticated.
|
||||
IsAuthenticated(ctx context.Context) error
|
||||
@@ -103,6 +104,7 @@ type Client interface {
|
||||
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 {
|
||||
// Returns true if this is a directory, false if it is a file.
|
||||
IsDirectory() bool
|
||||
@@ -142,6 +144,7 @@ type Commit struct {
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
//go:generate mockery --name CommitFile --structname MockCommitFile --inpackage --filename mock_commit_file.go --with-expecter
|
||||
type CommitFile interface {
|
||||
GetSHA() 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
|
||||
|
||||
import (
|
||||
context "context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -8,10 +9,12 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
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) {
|
||||
@@ -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",
|
||||
"factory.go",
|
||||
"impl.go",
|
||||
"mock_client.go",
|
||||
"mock_commit_file.go",
|
||||
"mock_repository_content.go",
|
||||
"testdata",
|
||||
"testdata/webhook-issue_comment-created.json",
|
||||
"testdata/webhook-ping-check.json",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package safepath
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
@@ -36,3 +37,29 @@ func Dir(filePath string) string {
|
||||
func InDir(filePath, dir string) bool {
|
||||
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