From 2eaeff8ea7cd1133c1cfbbb012a30ad70fb6ecbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roberto=20Jim=C3=A9nez=20S=C3=A1nchez?= Date: Thu, 10 Apr 2025 19:57:42 +0200 Subject: [PATCH] Provisioning: Fix issue with double prefix for github repository (#103781) --- .../provisioning/jobs/sync/incremental.go | 2 +- .../jobs/sync/incremental_test.go | 2 +- .../apis/provisioning/repository/github.go | 76 +- .../provisioning/repository/github/client.go | 3 + .../repository/github/mock_client.go | 1216 +++++++++++++++++ .../repository/github/mock_commit_file.go | 212 +++ .../github/mock_repository_content.go | 312 +++++ .../provisioning/repository/github_test.go | 101 ++ .../provisioning/repository/local_test.go | 3 + .../apis/provisioning/safepath/dir.go | 27 + .../apis/provisioning/safepath/dir_test.go | 98 ++ 11 files changed, 2038 insertions(+), 14 deletions(-) create mode 100644 pkg/registry/apis/provisioning/repository/github/mock_client.go create mode 100644 pkg/registry/apis/provisioning/repository/github/mock_commit_file.go create mode 100644 pkg/registry/apis/provisioning/repository/github/mock_repository_content.go diff --git a/pkg/registry/apis/provisioning/jobs/sync/incremental.go b/pkg/registry/apis/provisioning/jobs/sync/incremental.go index 43aafada339..9bbfdb43921 100644 --- a/pkg/registry/apis/provisioning/jobs/sync/incremental.go +++ b/pkg/registry/apis/provisioning/jobs/sync/incremental.go @@ -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 } diff --git a/pkg/registry/apis/provisioning/jobs/sync/incremental_test.go b/pkg/registry/apis/provisioning/jobs/sync/incremental_test.go index 03c75d898e5..8cf8906470b 100644 --- a/pkg/registry/apis/provisioning/jobs/sync/incremental_test.go +++ b/pkg/registry/apis/provisioning/jobs/sync/incremental_test.go @@ -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 diff --git a/pkg/registry/apis/provisioning/repository/github.go b/pkg/registry/apis/provisioning/repository/github.go index 4e8e75ba472..7da5d961409 100644 --- a/pkg/registry/apis/provisioning/repository/github.go +++ b/pkg/registry/apis/provisioning/repository/github.go @@ -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 diff --git a/pkg/registry/apis/provisioning/repository/github/client.go b/pkg/registry/apis/provisioning/repository/github/client.go index 3afe76b3172..943057463cc 100644 --- a/pkg/registry/apis/provisioning/repository/github/client.go +++ b/pkg/registry/apis/provisioning/repository/github/client.go @@ -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 diff --git a/pkg/registry/apis/provisioning/repository/github/mock_client.go b/pkg/registry/apis/provisioning/repository/github/mock_client.go new file mode 100644 index 00000000000..a564d1c4d52 --- /dev/null +++ b/pkg/registry/apis/provisioning/repository/github/mock_client.go @@ -0,0 +1,1216 @@ +// Code generated by mockery v2.52.4. DO NOT EDIT. + +package github + +import ( + context "context" + + mock "github.com/stretchr/testify/mock" +) + +// MockClient is an autogenerated mock type for the Client type +type MockClient struct { + mock.Mock +} + +type MockClient_Expecter struct { + mock *mock.Mock +} + +func (_m *MockClient) EXPECT() *MockClient_Expecter { + return &MockClient_Expecter{mock: &_m.Mock} +} + +// BranchExists provides a mock function with given fields: ctx, owner, repository, branchName +func (_m *MockClient) BranchExists(ctx context.Context, owner string, repository string, branchName string) (bool, error) { + ret := _m.Called(ctx, owner, repository, branchName) + + if len(ret) == 0 { + panic("no return value specified for BranchExists") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, string) (bool, error)); ok { + return rf(ctx, owner, repository, branchName) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, string) bool); ok { + r0 = rf(ctx, owner, repository, branchName) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, string) error); ok { + r1 = rf(ctx, owner, repository, branchName) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockClient_BranchExists_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BranchExists' +type MockClient_BranchExists_Call struct { + *mock.Call +} + +// BranchExists is a helper method to define mock.On call +// - ctx context.Context +// - owner string +// - repository string +// - branchName string +func (_e *MockClient_Expecter) BranchExists(ctx interface{}, owner interface{}, repository interface{}, branchName interface{}) *MockClient_BranchExists_Call { + return &MockClient_BranchExists_Call{Call: _e.mock.On("BranchExists", ctx, owner, repository, branchName)} +} + +func (_c *MockClient_BranchExists_Call) Run(run func(ctx context.Context, owner string, repository string, branchName string)) *MockClient_BranchExists_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(string)) + }) + return _c +} + +func (_c *MockClient_BranchExists_Call) Return(_a0 bool, _a1 error) *MockClient_BranchExists_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockClient_BranchExists_Call) RunAndReturn(run func(context.Context, string, string, string) (bool, error)) *MockClient_BranchExists_Call { + _c.Call.Return(run) + return _c +} + +// ClearAllPullRequestFileComments provides a mock function with given fields: ctx, owner, repository, number +func (_m *MockClient) ClearAllPullRequestFileComments(ctx context.Context, owner string, repository string, number int) error { + ret := _m.Called(ctx, owner, repository, number) + + if len(ret) == 0 { + panic("no return value specified for ClearAllPullRequestFileComments") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, int) error); ok { + r0 = rf(ctx, owner, repository, number) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockClient_ClearAllPullRequestFileComments_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ClearAllPullRequestFileComments' +type MockClient_ClearAllPullRequestFileComments_Call struct { + *mock.Call +} + +// ClearAllPullRequestFileComments is a helper method to define mock.On call +// - ctx context.Context +// - owner string +// - repository string +// - number int +func (_e *MockClient_Expecter) ClearAllPullRequestFileComments(ctx interface{}, owner interface{}, repository interface{}, number interface{}) *MockClient_ClearAllPullRequestFileComments_Call { + return &MockClient_ClearAllPullRequestFileComments_Call{Call: _e.mock.On("ClearAllPullRequestFileComments", ctx, owner, repository, number)} +} + +func (_c *MockClient_ClearAllPullRequestFileComments_Call) Run(run func(ctx context.Context, owner string, repository string, number int)) *MockClient_ClearAllPullRequestFileComments_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(int)) + }) + return _c +} + +func (_c *MockClient_ClearAllPullRequestFileComments_Call) Return(_a0 error) *MockClient_ClearAllPullRequestFileComments_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockClient_ClearAllPullRequestFileComments_Call) RunAndReturn(run func(context.Context, string, string, int) error) *MockClient_ClearAllPullRequestFileComments_Call { + _c.Call.Return(run) + return _c +} + +// Commits provides a mock function with given fields: ctx, owner, repository, path, branch +func (_m *MockClient) Commits(ctx context.Context, owner string, repository string, path string, branch string) ([]Commit, error) { + ret := _m.Called(ctx, owner, repository, path, branch) + + if len(ret) == 0 { + panic("no return value specified for Commits") + } + + var r0 []Commit + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string) ([]Commit, error)); ok { + return rf(ctx, owner, repository, path, branch) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string) []Commit); ok { + r0 = rf(ctx, owner, repository, path, branch) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]Commit) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, string, string) error); ok { + r1 = rf(ctx, owner, repository, path, branch) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockClient_Commits_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Commits' +type MockClient_Commits_Call struct { + *mock.Call +} + +// Commits is a helper method to define mock.On call +// - ctx context.Context +// - owner string +// - repository string +// - path string +// - branch string +func (_e *MockClient_Expecter) Commits(ctx interface{}, owner interface{}, repository interface{}, path interface{}, branch interface{}) *MockClient_Commits_Call { + return &MockClient_Commits_Call{Call: _e.mock.On("Commits", ctx, owner, repository, path, branch)} +} + +func (_c *MockClient_Commits_Call) Run(run func(ctx context.Context, owner string, repository string, path string, branch string)) *MockClient_Commits_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(string), args[4].(string)) + }) + return _c +} + +func (_c *MockClient_Commits_Call) Return(_a0 []Commit, _a1 error) *MockClient_Commits_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockClient_Commits_Call) RunAndReturn(run func(context.Context, string, string, string, string) ([]Commit, error)) *MockClient_Commits_Call { + _c.Call.Return(run) + return _c +} + +// CompareCommits provides a mock function with given fields: ctx, owner, repository, base, head +func (_m *MockClient) CompareCommits(ctx context.Context, owner string, repository string, base string, head string) ([]CommitFile, error) { + ret := _m.Called(ctx, owner, repository, base, head) + + if len(ret) == 0 { + panic("no return value specified for CompareCommits") + } + + var r0 []CommitFile + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string) ([]CommitFile, error)); ok { + return rf(ctx, owner, repository, base, head) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string) []CommitFile); ok { + r0 = rf(ctx, owner, repository, base, head) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]CommitFile) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, string, string) error); ok { + r1 = rf(ctx, owner, repository, base, head) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockClient_CompareCommits_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CompareCommits' +type MockClient_CompareCommits_Call struct { + *mock.Call +} + +// CompareCommits is a helper method to define mock.On call +// - ctx context.Context +// - owner string +// - repository string +// - base string +// - head string +func (_e *MockClient_Expecter) CompareCommits(ctx interface{}, owner interface{}, repository interface{}, base interface{}, head interface{}) *MockClient_CompareCommits_Call { + return &MockClient_CompareCommits_Call{Call: _e.mock.On("CompareCommits", ctx, owner, repository, base, head)} +} + +func (_c *MockClient_CompareCommits_Call) Run(run func(ctx context.Context, owner string, repository string, base string, head string)) *MockClient_CompareCommits_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(string), args[4].(string)) + }) + return _c +} + +func (_c *MockClient_CompareCommits_Call) Return(_a0 []CommitFile, _a1 error) *MockClient_CompareCommits_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockClient_CompareCommits_Call) RunAndReturn(run func(context.Context, string, string, string, string) ([]CommitFile, error)) *MockClient_CompareCommits_Call { + _c.Call.Return(run) + return _c +} + +// CreateBranch provides a mock function with given fields: ctx, owner, repository, sourceBranch, branchName +func (_m *MockClient) CreateBranch(ctx context.Context, owner string, repository string, sourceBranch string, branchName string) error { + ret := _m.Called(ctx, owner, repository, sourceBranch, branchName) + + if len(ret) == 0 { + panic("no return value specified for CreateBranch") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string) error); ok { + r0 = rf(ctx, owner, repository, sourceBranch, branchName) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockClient_CreateBranch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateBranch' +type MockClient_CreateBranch_Call struct { + *mock.Call +} + +// CreateBranch is a helper method to define mock.On call +// - ctx context.Context +// - owner string +// - repository string +// - sourceBranch string +// - branchName string +func (_e *MockClient_Expecter) CreateBranch(ctx interface{}, owner interface{}, repository interface{}, sourceBranch interface{}, branchName interface{}) *MockClient_CreateBranch_Call { + return &MockClient_CreateBranch_Call{Call: _e.mock.On("CreateBranch", ctx, owner, repository, sourceBranch, branchName)} +} + +func (_c *MockClient_CreateBranch_Call) Run(run func(ctx context.Context, owner string, repository string, sourceBranch string, branchName string)) *MockClient_CreateBranch_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(string), args[4].(string)) + }) + return _c +} + +func (_c *MockClient_CreateBranch_Call) Return(_a0 error) *MockClient_CreateBranch_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockClient_CreateBranch_Call) RunAndReturn(run func(context.Context, string, string, string, string) error) *MockClient_CreateBranch_Call { + _c.Call.Return(run) + return _c +} + +// CreateFile provides a mock function with given fields: ctx, owner, repository, path, branch, message, content +func (_m *MockClient) CreateFile(ctx context.Context, owner string, repository string, path string, branch string, message string, content []byte) error { + ret := _m.Called(ctx, owner, repository, path, branch, message, content) + + if len(ret) == 0 { + panic("no return value specified for CreateFile") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string, string, []byte) error); ok { + r0 = rf(ctx, owner, repository, path, branch, message, content) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockClient_CreateFile_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateFile' +type MockClient_CreateFile_Call struct { + *mock.Call +} + +// CreateFile is a helper method to define mock.On call +// - ctx context.Context +// - owner string +// - repository string +// - path string +// - branch string +// - message string +// - content []byte +func (_e *MockClient_Expecter) CreateFile(ctx interface{}, owner interface{}, repository interface{}, path interface{}, branch interface{}, message interface{}, content interface{}) *MockClient_CreateFile_Call { + return &MockClient_CreateFile_Call{Call: _e.mock.On("CreateFile", ctx, owner, repository, path, branch, message, content)} +} + +func (_c *MockClient_CreateFile_Call) Run(run func(ctx context.Context, owner string, repository string, path string, branch string, message string, content []byte)) *MockClient_CreateFile_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(string), args[4].(string), args[5].(string), args[6].([]byte)) + }) + return _c +} + +func (_c *MockClient_CreateFile_Call) Return(_a0 error) *MockClient_CreateFile_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockClient_CreateFile_Call) RunAndReturn(run func(context.Context, string, string, string, string, string, []byte) error) *MockClient_CreateFile_Call { + _c.Call.Return(run) + return _c +} + +// CreatePullRequestComment provides a mock function with given fields: ctx, owner, repository, number, body +func (_m *MockClient) CreatePullRequestComment(ctx context.Context, owner string, repository string, number int, body string) error { + ret := _m.Called(ctx, owner, repository, number, body) + + if len(ret) == 0 { + panic("no return value specified for CreatePullRequestComment") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, int, string) error); ok { + r0 = rf(ctx, owner, repository, number, body) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockClient_CreatePullRequestComment_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreatePullRequestComment' +type MockClient_CreatePullRequestComment_Call struct { + *mock.Call +} + +// CreatePullRequestComment is a helper method to define mock.On call +// - ctx context.Context +// - owner string +// - repository string +// - number int +// - body string +func (_e *MockClient_Expecter) CreatePullRequestComment(ctx interface{}, owner interface{}, repository interface{}, number interface{}, body interface{}) *MockClient_CreatePullRequestComment_Call { + return &MockClient_CreatePullRequestComment_Call{Call: _e.mock.On("CreatePullRequestComment", ctx, owner, repository, number, body)} +} + +func (_c *MockClient_CreatePullRequestComment_Call) Run(run func(ctx context.Context, owner string, repository string, number int, body string)) *MockClient_CreatePullRequestComment_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(int), args[4].(string)) + }) + return _c +} + +func (_c *MockClient_CreatePullRequestComment_Call) Return(_a0 error) *MockClient_CreatePullRequestComment_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockClient_CreatePullRequestComment_Call) RunAndReturn(run func(context.Context, string, string, int, string) error) *MockClient_CreatePullRequestComment_Call { + _c.Call.Return(run) + return _c +} + +// CreatePullRequestFileComment provides a mock function with given fields: ctx, owner, repository, number, comment +func (_m *MockClient) CreatePullRequestFileComment(ctx context.Context, owner string, repository string, number int, comment FileComment) error { + ret := _m.Called(ctx, owner, repository, number, comment) + + if len(ret) == 0 { + panic("no return value specified for CreatePullRequestFileComment") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, int, FileComment) error); ok { + r0 = rf(ctx, owner, repository, number, comment) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockClient_CreatePullRequestFileComment_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreatePullRequestFileComment' +type MockClient_CreatePullRequestFileComment_Call struct { + *mock.Call +} + +// CreatePullRequestFileComment is a helper method to define mock.On call +// - ctx context.Context +// - owner string +// - repository string +// - number int +// - comment FileComment +func (_e *MockClient_Expecter) CreatePullRequestFileComment(ctx interface{}, owner interface{}, repository interface{}, number interface{}, comment interface{}) *MockClient_CreatePullRequestFileComment_Call { + return &MockClient_CreatePullRequestFileComment_Call{Call: _e.mock.On("CreatePullRequestFileComment", ctx, owner, repository, number, comment)} +} + +func (_c *MockClient_CreatePullRequestFileComment_Call) Run(run func(ctx context.Context, owner string, repository string, number int, comment FileComment)) *MockClient_CreatePullRequestFileComment_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(int), args[4].(FileComment)) + }) + return _c +} + +func (_c *MockClient_CreatePullRequestFileComment_Call) Return(_a0 error) *MockClient_CreatePullRequestFileComment_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockClient_CreatePullRequestFileComment_Call) RunAndReturn(run func(context.Context, string, string, int, FileComment) error) *MockClient_CreatePullRequestFileComment_Call { + _c.Call.Return(run) + return _c +} + +// CreateWebhook provides a mock function with given fields: ctx, owner, repository, cfg +func (_m *MockClient) CreateWebhook(ctx context.Context, owner string, repository string, cfg WebhookConfig) (WebhookConfig, error) { + ret := _m.Called(ctx, owner, repository, cfg) + + if len(ret) == 0 { + panic("no return value specified for CreateWebhook") + } + + var r0 WebhookConfig + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, WebhookConfig) (WebhookConfig, error)); ok { + return rf(ctx, owner, repository, cfg) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, WebhookConfig) WebhookConfig); ok { + r0 = rf(ctx, owner, repository, cfg) + } else { + r0 = ret.Get(0).(WebhookConfig) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, WebhookConfig) error); ok { + r1 = rf(ctx, owner, repository, cfg) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockClient_CreateWebhook_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateWebhook' +type MockClient_CreateWebhook_Call struct { + *mock.Call +} + +// CreateWebhook is a helper method to define mock.On call +// - ctx context.Context +// - owner string +// - repository string +// - cfg WebhookConfig +func (_e *MockClient_Expecter) CreateWebhook(ctx interface{}, owner interface{}, repository interface{}, cfg interface{}) *MockClient_CreateWebhook_Call { + return &MockClient_CreateWebhook_Call{Call: _e.mock.On("CreateWebhook", ctx, owner, repository, cfg)} +} + +func (_c *MockClient_CreateWebhook_Call) Run(run func(ctx context.Context, owner string, repository string, cfg WebhookConfig)) *MockClient_CreateWebhook_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(WebhookConfig)) + }) + return _c +} + +func (_c *MockClient_CreateWebhook_Call) Return(_a0 WebhookConfig, _a1 error) *MockClient_CreateWebhook_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockClient_CreateWebhook_Call) RunAndReturn(run func(context.Context, string, string, WebhookConfig) (WebhookConfig, error)) *MockClient_CreateWebhook_Call { + _c.Call.Return(run) + return _c +} + +// DeleteFile provides a mock function with given fields: ctx, owner, repository, path, branch, message, hash +func (_m *MockClient) DeleteFile(ctx context.Context, owner string, repository string, path string, branch string, message string, hash string) error { + ret := _m.Called(ctx, owner, repository, path, branch, message, hash) + + if len(ret) == 0 { + panic("no return value specified for DeleteFile") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string, string, string) error); ok { + r0 = rf(ctx, owner, repository, path, branch, message, hash) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockClient_DeleteFile_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteFile' +type MockClient_DeleteFile_Call struct { + *mock.Call +} + +// DeleteFile is a helper method to define mock.On call +// - ctx context.Context +// - owner string +// - repository string +// - path string +// - branch string +// - message string +// - hash string +func (_e *MockClient_Expecter) DeleteFile(ctx interface{}, owner interface{}, repository interface{}, path interface{}, branch interface{}, message interface{}, hash interface{}) *MockClient_DeleteFile_Call { + return &MockClient_DeleteFile_Call{Call: _e.mock.On("DeleteFile", ctx, owner, repository, path, branch, message, hash)} +} + +func (_c *MockClient_DeleteFile_Call) Run(run func(ctx context.Context, owner string, repository string, path string, branch string, message string, hash string)) *MockClient_DeleteFile_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(string), args[4].(string), args[5].(string), args[6].(string)) + }) + return _c +} + +func (_c *MockClient_DeleteFile_Call) Return(_a0 error) *MockClient_DeleteFile_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockClient_DeleteFile_Call) RunAndReturn(run func(context.Context, string, string, string, string, string, string) error) *MockClient_DeleteFile_Call { + _c.Call.Return(run) + return _c +} + +// DeleteWebhook provides a mock function with given fields: ctx, owner, repository, webhookID +func (_m *MockClient) DeleteWebhook(ctx context.Context, owner string, repository string, webhookID int64) error { + ret := _m.Called(ctx, owner, repository, webhookID) + + if len(ret) == 0 { + panic("no return value specified for DeleteWebhook") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, int64) error); ok { + r0 = rf(ctx, owner, repository, webhookID) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockClient_DeleteWebhook_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteWebhook' +type MockClient_DeleteWebhook_Call struct { + *mock.Call +} + +// DeleteWebhook is a helper method to define mock.On call +// - ctx context.Context +// - owner string +// - repository string +// - webhookID int64 +func (_e *MockClient_Expecter) DeleteWebhook(ctx interface{}, owner interface{}, repository interface{}, webhookID interface{}) *MockClient_DeleteWebhook_Call { + return &MockClient_DeleteWebhook_Call{Call: _e.mock.On("DeleteWebhook", ctx, owner, repository, webhookID)} +} + +func (_c *MockClient_DeleteWebhook_Call) Run(run func(ctx context.Context, owner string, repository string, webhookID int64)) *MockClient_DeleteWebhook_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(int64)) + }) + return _c +} + +func (_c *MockClient_DeleteWebhook_Call) Return(_a0 error) *MockClient_DeleteWebhook_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockClient_DeleteWebhook_Call) RunAndReturn(run func(context.Context, string, string, int64) error) *MockClient_DeleteWebhook_Call { + _c.Call.Return(run) + return _c +} + +// EditWebhook provides a mock function with given fields: ctx, owner, repository, cfg +func (_m *MockClient) EditWebhook(ctx context.Context, owner string, repository string, cfg WebhookConfig) error { + ret := _m.Called(ctx, owner, repository, cfg) + + if len(ret) == 0 { + panic("no return value specified for EditWebhook") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, WebhookConfig) error); ok { + r0 = rf(ctx, owner, repository, cfg) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockClient_EditWebhook_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EditWebhook' +type MockClient_EditWebhook_Call struct { + *mock.Call +} + +// EditWebhook is a helper method to define mock.On call +// - ctx context.Context +// - owner string +// - repository string +// - cfg WebhookConfig +func (_e *MockClient_Expecter) EditWebhook(ctx interface{}, owner interface{}, repository interface{}, cfg interface{}) *MockClient_EditWebhook_Call { + return &MockClient_EditWebhook_Call{Call: _e.mock.On("EditWebhook", ctx, owner, repository, cfg)} +} + +func (_c *MockClient_EditWebhook_Call) Run(run func(ctx context.Context, owner string, repository string, cfg WebhookConfig)) *MockClient_EditWebhook_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(WebhookConfig)) + }) + return _c +} + +func (_c *MockClient_EditWebhook_Call) Return(_a0 error) *MockClient_EditWebhook_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockClient_EditWebhook_Call) RunAndReturn(run func(context.Context, string, string, WebhookConfig) error) *MockClient_EditWebhook_Call { + _c.Call.Return(run) + return _c +} + +// GetBranch provides a mock function with given fields: ctx, owner, repository, branchName +func (_m *MockClient) GetBranch(ctx context.Context, owner string, repository string, branchName string) (Branch, error) { + ret := _m.Called(ctx, owner, repository, branchName) + + if len(ret) == 0 { + panic("no return value specified for GetBranch") + } + + var r0 Branch + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, string) (Branch, error)); ok { + return rf(ctx, owner, repository, branchName) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, string) Branch); ok { + r0 = rf(ctx, owner, repository, branchName) + } else { + r0 = ret.Get(0).(Branch) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, string) error); ok { + r1 = rf(ctx, owner, repository, branchName) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockClient_GetBranch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetBranch' +type MockClient_GetBranch_Call struct { + *mock.Call +} + +// GetBranch is a helper method to define mock.On call +// - ctx context.Context +// - owner string +// - repository string +// - branchName string +func (_e *MockClient_Expecter) GetBranch(ctx interface{}, owner interface{}, repository interface{}, branchName interface{}) *MockClient_GetBranch_Call { + return &MockClient_GetBranch_Call{Call: _e.mock.On("GetBranch", ctx, owner, repository, branchName)} +} + +func (_c *MockClient_GetBranch_Call) Run(run func(ctx context.Context, owner string, repository string, branchName string)) *MockClient_GetBranch_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(string)) + }) + return _c +} + +func (_c *MockClient_GetBranch_Call) Return(_a0 Branch, _a1 error) *MockClient_GetBranch_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockClient_GetBranch_Call) RunAndReturn(run func(context.Context, string, string, string) (Branch, error)) *MockClient_GetBranch_Call { + _c.Call.Return(run) + return _c +} + +// GetContents provides a mock function with given fields: ctx, owner, repository, path, ref +func (_m *MockClient) GetContents(ctx context.Context, owner string, repository string, path string, ref string) (RepositoryContent, []RepositoryContent, error) { + ret := _m.Called(ctx, owner, repository, path, ref) + + if len(ret) == 0 { + panic("no return value specified for GetContents") + } + + var r0 RepositoryContent + var r1 []RepositoryContent + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string) (RepositoryContent, []RepositoryContent, error)); ok { + return rf(ctx, owner, repository, path, ref) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string) RepositoryContent); ok { + r0 = rf(ctx, owner, repository, path, ref) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(RepositoryContent) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, string, string) []RepositoryContent); ok { + r1 = rf(ctx, owner, repository, path, ref) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).([]RepositoryContent) + } + } + + if rf, ok := ret.Get(2).(func(context.Context, string, string, string, string) error); ok { + r2 = rf(ctx, owner, repository, path, ref) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// MockClient_GetContents_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetContents' +type MockClient_GetContents_Call struct { + *mock.Call +} + +// GetContents is a helper method to define mock.On call +// - ctx context.Context +// - owner string +// - repository string +// - path string +// - ref string +func (_e *MockClient_Expecter) GetContents(ctx interface{}, owner interface{}, repository interface{}, path interface{}, ref interface{}) *MockClient_GetContents_Call { + return &MockClient_GetContents_Call{Call: _e.mock.On("GetContents", ctx, owner, repository, path, ref)} +} + +func (_c *MockClient_GetContents_Call) Run(run func(ctx context.Context, owner string, repository string, path string, ref string)) *MockClient_GetContents_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(string), args[4].(string)) + }) + return _c +} + +func (_c *MockClient_GetContents_Call) Return(fileContents RepositoryContent, dirContents []RepositoryContent, err error) *MockClient_GetContents_Call { + _c.Call.Return(fileContents, dirContents, err) + return _c +} + +func (_c *MockClient_GetContents_Call) RunAndReturn(run func(context.Context, string, string, string, string) (RepositoryContent, []RepositoryContent, error)) *MockClient_GetContents_Call { + _c.Call.Return(run) + return _c +} + +// GetTree provides a mock function with given fields: ctx, owner, repository, basePath, ref, recursive +func (_m *MockClient) GetTree(ctx context.Context, owner string, repository string, basePath string, ref string, recursive bool) ([]RepositoryContent, bool, error) { + ret := _m.Called(ctx, owner, repository, basePath, ref, recursive) + + if len(ret) == 0 { + panic("no return value specified for GetTree") + } + + var r0 []RepositoryContent + var r1 bool + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string, bool) ([]RepositoryContent, bool, error)); ok { + return rf(ctx, owner, repository, basePath, ref, recursive) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string, bool) []RepositoryContent); ok { + r0 = rf(ctx, owner, repository, basePath, ref, recursive) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]RepositoryContent) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, string, string, bool) bool); ok { + r1 = rf(ctx, owner, repository, basePath, ref, recursive) + } else { + r1 = ret.Get(1).(bool) + } + + if rf, ok := ret.Get(2).(func(context.Context, string, string, string, string, bool) error); ok { + r2 = rf(ctx, owner, repository, basePath, ref, recursive) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// MockClient_GetTree_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTree' +type MockClient_GetTree_Call struct { + *mock.Call +} + +// GetTree is a helper method to define mock.On call +// - ctx context.Context +// - owner string +// - repository string +// - basePath string +// - ref string +// - recursive bool +func (_e *MockClient_Expecter) GetTree(ctx interface{}, owner interface{}, repository interface{}, basePath interface{}, ref interface{}, recursive interface{}) *MockClient_GetTree_Call { + return &MockClient_GetTree_Call{Call: _e.mock.On("GetTree", ctx, owner, repository, basePath, ref, recursive)} +} + +func (_c *MockClient_GetTree_Call) Run(run func(ctx context.Context, owner string, repository string, basePath string, ref string, recursive bool)) *MockClient_GetTree_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(string), args[4].(string), args[5].(bool)) + }) + return _c +} + +func (_c *MockClient_GetTree_Call) Return(entries []RepositoryContent, truncated bool, err error) *MockClient_GetTree_Call { + _c.Call.Return(entries, truncated, err) + return _c +} + +func (_c *MockClient_GetTree_Call) RunAndReturn(run func(context.Context, string, string, string, string, bool) ([]RepositoryContent, bool, error)) *MockClient_GetTree_Call { + _c.Call.Return(run) + return _c +} + +// GetWebhook provides a mock function with given fields: ctx, owner, repository, webhookID +func (_m *MockClient) GetWebhook(ctx context.Context, owner string, repository string, webhookID int64) (WebhookConfig, error) { + ret := _m.Called(ctx, owner, repository, webhookID) + + if len(ret) == 0 { + panic("no return value specified for GetWebhook") + } + + var r0 WebhookConfig + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, int64) (WebhookConfig, error)); ok { + return rf(ctx, owner, repository, webhookID) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, int64) WebhookConfig); ok { + r0 = rf(ctx, owner, repository, webhookID) + } else { + r0 = ret.Get(0).(WebhookConfig) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, int64) error); ok { + r1 = rf(ctx, owner, repository, webhookID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockClient_GetWebhook_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetWebhook' +type MockClient_GetWebhook_Call struct { + *mock.Call +} + +// GetWebhook is a helper method to define mock.On call +// - ctx context.Context +// - owner string +// - repository string +// - webhookID int64 +func (_e *MockClient_Expecter) GetWebhook(ctx interface{}, owner interface{}, repository interface{}, webhookID interface{}) *MockClient_GetWebhook_Call { + return &MockClient_GetWebhook_Call{Call: _e.mock.On("GetWebhook", ctx, owner, repository, webhookID)} +} + +func (_c *MockClient_GetWebhook_Call) Run(run func(ctx context.Context, owner string, repository string, webhookID int64)) *MockClient_GetWebhook_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(int64)) + }) + return _c +} + +func (_c *MockClient_GetWebhook_Call) Return(_a0 WebhookConfig, _a1 error) *MockClient_GetWebhook_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockClient_GetWebhook_Call) RunAndReturn(run func(context.Context, string, string, int64) (WebhookConfig, error)) *MockClient_GetWebhook_Call { + _c.Call.Return(run) + return _c +} + +// IsAuthenticated provides a mock function with given fields: ctx +func (_m *MockClient) IsAuthenticated(ctx context.Context) error { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for IsAuthenticated") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockClient_IsAuthenticated_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsAuthenticated' +type MockClient_IsAuthenticated_Call struct { + *mock.Call +} + +// IsAuthenticated is a helper method to define mock.On call +// - ctx context.Context +func (_e *MockClient_Expecter) IsAuthenticated(ctx interface{}) *MockClient_IsAuthenticated_Call { + return &MockClient_IsAuthenticated_Call{Call: _e.mock.On("IsAuthenticated", ctx)} +} + +func (_c *MockClient_IsAuthenticated_Call) Run(run func(ctx context.Context)) *MockClient_IsAuthenticated_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *MockClient_IsAuthenticated_Call) Return(_a0 error) *MockClient_IsAuthenticated_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockClient_IsAuthenticated_Call) RunAndReturn(run func(context.Context) error) *MockClient_IsAuthenticated_Call { + _c.Call.Return(run) + return _c +} + +// ListPullRequestFiles provides a mock function with given fields: ctx, owner, repository, number +func (_m *MockClient) ListPullRequestFiles(ctx context.Context, owner string, repository string, number int) ([]CommitFile, error) { + ret := _m.Called(ctx, owner, repository, number) + + if len(ret) == 0 { + panic("no return value specified for ListPullRequestFiles") + } + + var r0 []CommitFile + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, int) ([]CommitFile, error)); ok { + return rf(ctx, owner, repository, number) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, int) []CommitFile); ok { + r0 = rf(ctx, owner, repository, number) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]CommitFile) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, int) error); ok { + r1 = rf(ctx, owner, repository, number) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockClient_ListPullRequestFiles_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListPullRequestFiles' +type MockClient_ListPullRequestFiles_Call struct { + *mock.Call +} + +// ListPullRequestFiles is a helper method to define mock.On call +// - ctx context.Context +// - owner string +// - repository string +// - number int +func (_e *MockClient_Expecter) ListPullRequestFiles(ctx interface{}, owner interface{}, repository interface{}, number interface{}) *MockClient_ListPullRequestFiles_Call { + return &MockClient_ListPullRequestFiles_Call{Call: _e.mock.On("ListPullRequestFiles", ctx, owner, repository, number)} +} + +func (_c *MockClient_ListPullRequestFiles_Call) Run(run func(ctx context.Context, owner string, repository string, number int)) *MockClient_ListPullRequestFiles_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(int)) + }) + return _c +} + +func (_c *MockClient_ListPullRequestFiles_Call) Return(_a0 []CommitFile, _a1 error) *MockClient_ListPullRequestFiles_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockClient_ListPullRequestFiles_Call) RunAndReturn(run func(context.Context, string, string, int) ([]CommitFile, error)) *MockClient_ListPullRequestFiles_Call { + _c.Call.Return(run) + return _c +} + +// ListWebhooks provides a mock function with given fields: ctx, owner, repository +func (_m *MockClient) ListWebhooks(ctx context.Context, owner string, repository string) ([]WebhookConfig, error) { + ret := _m.Called(ctx, owner, repository) + + if len(ret) == 0 { + panic("no return value specified for ListWebhooks") + } + + var r0 []WebhookConfig + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) ([]WebhookConfig, error)); ok { + return rf(ctx, owner, repository) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string) []WebhookConfig); ok { + r0 = rf(ctx, owner, repository) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]WebhookConfig) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { + r1 = rf(ctx, owner, repository) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockClient_ListWebhooks_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListWebhooks' +type MockClient_ListWebhooks_Call struct { + *mock.Call +} + +// ListWebhooks is a helper method to define mock.On call +// - ctx context.Context +// - owner string +// - repository string +func (_e *MockClient_Expecter) ListWebhooks(ctx interface{}, owner interface{}, repository interface{}) *MockClient_ListWebhooks_Call { + return &MockClient_ListWebhooks_Call{Call: _e.mock.On("ListWebhooks", ctx, owner, repository)} +} + +func (_c *MockClient_ListWebhooks_Call) Run(run func(ctx context.Context, owner string, repository string)) *MockClient_ListWebhooks_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string)) + }) + return _c +} + +func (_c *MockClient_ListWebhooks_Call) Return(_a0 []WebhookConfig, _a1 error) *MockClient_ListWebhooks_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockClient_ListWebhooks_Call) RunAndReturn(run func(context.Context, string, string) ([]WebhookConfig, error)) *MockClient_ListWebhooks_Call { + _c.Call.Return(run) + return _c +} + +// RepoExists provides a mock function with given fields: ctx, owner, repository +func (_m *MockClient) RepoExists(ctx context.Context, owner string, repository string) (bool, error) { + ret := _m.Called(ctx, owner, repository) + + if len(ret) == 0 { + panic("no return value specified for RepoExists") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) (bool, error)); ok { + return rf(ctx, owner, repository) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string) bool); ok { + r0 = rf(ctx, owner, repository) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { + r1 = rf(ctx, owner, repository) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockClient_RepoExists_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RepoExists' +type MockClient_RepoExists_Call struct { + *mock.Call +} + +// RepoExists is a helper method to define mock.On call +// - ctx context.Context +// - owner string +// - repository string +func (_e *MockClient_Expecter) RepoExists(ctx interface{}, owner interface{}, repository interface{}) *MockClient_RepoExists_Call { + return &MockClient_RepoExists_Call{Call: _e.mock.On("RepoExists", ctx, owner, repository)} +} + +func (_c *MockClient_RepoExists_Call) Run(run func(ctx context.Context, owner string, repository string)) *MockClient_RepoExists_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string)) + }) + return _c +} + +func (_c *MockClient_RepoExists_Call) Return(_a0 bool, _a1 error) *MockClient_RepoExists_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockClient_RepoExists_Call) RunAndReturn(run func(context.Context, string, string) (bool, error)) *MockClient_RepoExists_Call { + _c.Call.Return(run) + return _c +} + +// UpdateFile provides a mock function with given fields: ctx, owner, repository, path, branch, message, hash, content +func (_m *MockClient) UpdateFile(ctx context.Context, owner string, repository string, path string, branch string, message string, hash string, content []byte) error { + ret := _m.Called(ctx, owner, repository, path, branch, message, hash, content) + + if len(ret) == 0 { + panic("no return value specified for UpdateFile") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string, string, string, []byte) error); ok { + r0 = rf(ctx, owner, repository, path, branch, message, hash, content) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockClient_UpdateFile_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateFile' +type MockClient_UpdateFile_Call struct { + *mock.Call +} + +// UpdateFile is a helper method to define mock.On call +// - ctx context.Context +// - owner string +// - repository string +// - path string +// - branch string +// - message string +// - hash string +// - content []byte +func (_e *MockClient_Expecter) UpdateFile(ctx interface{}, owner interface{}, repository interface{}, path interface{}, branch interface{}, message interface{}, hash interface{}, content interface{}) *MockClient_UpdateFile_Call { + return &MockClient_UpdateFile_Call{Call: _e.mock.On("UpdateFile", ctx, owner, repository, path, branch, message, hash, content)} +} + +func (_c *MockClient_UpdateFile_Call) Run(run func(ctx context.Context, owner string, repository string, path string, branch string, message string, hash string, content []byte)) *MockClient_UpdateFile_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(string), args[4].(string), args[5].(string), args[6].(string), args[7].([]byte)) + }) + return _c +} + +func (_c *MockClient_UpdateFile_Call) Return(_a0 error) *MockClient_UpdateFile_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockClient_UpdateFile_Call) RunAndReturn(run func(context.Context, string, string, string, string, string, string, []byte) error) *MockClient_UpdateFile_Call { + _c.Call.Return(run) + return _c +} + +// NewMockClient creates a new instance of MockClient. 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 NewMockClient(t interface { + mock.TestingT + Cleanup(func()) +}) *MockClient { + mock := &MockClient{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/registry/apis/provisioning/repository/github/mock_commit_file.go b/pkg/registry/apis/provisioning/repository/github/mock_commit_file.go new file mode 100644 index 00000000000..68d3d2044f0 --- /dev/null +++ b/pkg/registry/apis/provisioning/repository/github/mock_commit_file.go @@ -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 +} diff --git a/pkg/registry/apis/provisioning/repository/github/mock_repository_content.go b/pkg/registry/apis/provisioning/repository/github/mock_repository_content.go new file mode 100644 index 00000000000..bddbaaf2558 --- /dev/null +++ b/pkg/registry/apis/provisioning/repository/github/mock_repository_content.go @@ -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 +} diff --git a/pkg/registry/apis/provisioning/repository/github_test.go b/pkg/registry/apis/provisioning/repository/github_test.go index 6ee181429d5..f6939510f87 100644 --- a/pkg/registry/apis/provisioning/repository/github_test.go +++ b/pkg/registry/apis/provisioning/repository/github_test.go @@ -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) + }) + } +} diff --git a/pkg/registry/apis/provisioning/repository/local_test.go b/pkg/registry/apis/provisioning/repository/local_test.go index 42e09e725f4..c2825ff4f84 100644 --- a/pkg/registry/apis/provisioning/repository/local_test.go +++ b/pkg/registry/apis/provisioning/repository/local_test.go @@ -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", diff --git a/pkg/registry/apis/provisioning/safepath/dir.go b/pkg/registry/apis/provisioning/safepath/dir.go index c6d0bbbe727..d156229df6c 100644 --- a/pkg/registry/apis/provisioning/safepath/dir.go +++ b/pkg/registry/apis/provisioning/safepath/dir.go @@ -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 +} diff --git a/pkg/registry/apis/provisioning/safepath/dir_test.go b/pkg/registry/apis/provisioning/safepath/dir_test.go index bf55b160660..2d88614243b 100644 --- a/pkg/registry/apis/provisioning/safepath/dir_test.go +++ b/pkg/registry/apis/provisioning/safepath/dir_test.go @@ -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) + } + }) + } +}