diff --git a/internal/commands/util/usercount/bitbucketserver/bitbucket-server.go b/internal/commands/util/usercount/bitbucketserver/bitbucket-server.go index 0ab16d287..4dccf2379 100644 --- a/internal/commands/util/usercount/bitbucketserver/bitbucket-server.go +++ b/internal/commands/util/usercount/bitbucketserver/bitbucket-server.go @@ -203,7 +203,8 @@ func searchRepos( *bitbucketServerToken, ) if err != nil { - return nil, nil, nil, err + log.Printf("Skipping repository %s/%s: Repository is corrupted (error: %v)", project, repo, err) + continue } totalCommits = append(totalCommits, commits...) diff --git a/internal/commands/util/usercount/bitbucketserver/bitbucket-server_test.go b/internal/commands/util/usercount/bitbucketserver/bitbucket-server_test.go new file mode 100644 index 000000000..f2b6c77ca --- /dev/null +++ b/internal/commands/util/usercount/bitbucketserver/bitbucket-server_test.go @@ -0,0 +1,48 @@ +package bitbucketserver + +import ( + "bytes" + "log" + "testing" + + "github.com/checkmarx/ast-cli/internal/wrappers/mock" + "gotest.tools/assert" +) + +func TestSearchReposWithCorruptedRepositories(t *testing.T) { + mockWrapper := mock.WrapperBitbucketServer{ + CorruptedRepos: []string{"repo-2"}, + } + + var logOutput bytes.Buffer + log.SetOutput(&logOutput) + defer log.SetOutput(nil) + + project := "mock-project" + repos := []string{"repo-1", "repo-2", "repo-3"} + token := "mock-token" + + views, viewsUsers, err := mockWrapper.SearchRepos(project, repos, token) + + assert.NilError(t, err, "SearchRepos should not return an error") + + assert.Equal(t, len(views), 2, "Only valid repositories should be processed") + assert.Equal(t, views[0].Name, "mock-project/repo-1", "First repository name should match") + assert.Equal(t, views[1].Name, "mock-project/repo-3", "Second repository name should match") + + assert.Equal(t, len(viewsUsers), 2, "Each repository should have 1 contributor") + assert.Equal(t, viewsUsers[0].Name, "mock-project/repo-1", "Contributor should match first repository") + assert.Equal(t, viewsUsers[1].Name, "mock-project/repo-3", "Contributor should match second repository") + + logStr := logOutput.String() + assert.Assert(t, containsLog(logStr, "Skipping repository mock-project/repo-2: Repository is corrupted"), "Log should contain corrupted repository message") + assert.Assert(t, containsLog(logStr, "Processed repository mock-project/repo-1"), "Log should confirm successful processing of repo-1") + assert.Assert(t, containsLog(logStr, "Processed repository mock-project/repo-3"), "Log should confirm successful processing of repo-3") + + t.Log("Captured Logs:") + t.Log(logStr) +} + +func containsLog(logStr, expected string) bool { + return bytes.Contains([]byte(logStr), []byte(expected)) +} diff --git a/internal/wrappers/mock/bitbucketServer-mock.go b/internal/wrappers/mock/bitbucketServer-mock.go new file mode 100644 index 000000000..b926302de --- /dev/null +++ b/internal/wrappers/mock/bitbucketServer-mock.go @@ -0,0 +1,85 @@ +package mock + +import ( + "fmt" + "log" + + "github.com/checkmarx/ast-cli/internal/wrappers/bitbucketserver" + "github.com/pkg/errors" +) + +type WrapperBitbucketServer struct { + CorruptedRepos []string +} + +type RepositoryView struct { + Name string `json:"name"` + UniqueContributors uint64 `json:"unique_contributors"` +} + +type UserView struct { + Name string `json:"name"` + UniqueContributorsUsername string `json:"unique_contributors_username"` +} + +func (m WrapperBitbucketServer) GetCommits(bitBucketURL, projectKey, repoSlug, bitBucketPassword string) ([]bitbucketserver.Commit, error) { + for _, corruptedRepo := range m.CorruptedRepos { + if repoSlug == corruptedRepo { + return nil, errors.New(fmt.Sprintf("repository %s is corrupted", repoSlug)) + } + } + return []bitbucketserver.Commit{ + { + Author: bitbucketserver.Author{Name: "Mock Author", Email: "mock-author@example.com"}, + AuthorTimestamp: 1625078400000, + }, + }, nil +} +func (m WrapperBitbucketServer) GetRepositories(bitBucketURL, projectKey, bitBucketPassword string) ([]bitbucketserver.Repo, error) { + return []bitbucketserver.Repo{ + {Slug: "repo-1", Name: "Repository 1"}, + {Slug: "repo-2", Name: "Repository 2"}, + {Slug: "repo-3", Name: "Repository 3"}, + }, nil +} +func (m WrapperBitbucketServer) GetProjects(bitBucketURL, bitBucketPassword string) ([]string, error) { + // Return mock projects + return []string{"project-1", "project-2"}, nil +} +func (m WrapperBitbucketServer) SearchRepos( + project string, + repos []string, + bitBucketToken string, +) ([]RepositoryView, []UserView, error) { + var views []RepositoryView + var viewsUsers []UserView + for _, repo := range repos { + _, err := m.GetCommits("mock-url", project, repo, bitBucketToken) + if err != nil { + log.Printf("Skipping repository %s/%s: Repository is corrupted (error: %v)", project, repo, err) + continue + } + log.Printf("Processed repository %s/%s", project, repo) + + uniqueContributors := map[string]string{ + "mock-email@example.com": "Mock Author", + } + views = append( + views, + RepositoryView{ + Name: fmt.Sprintf("%s/%s", project, repo), + UniqueContributors: uint64(len(uniqueContributors)), + }, + ) + for email, name := range uniqueContributors { + viewsUsers = append( + viewsUsers, + UserView{ + Name: fmt.Sprintf("%s/%s", project, repo), + UniqueContributorsUsername: fmt.Sprintf("%s - %s", name, email), + }, + ) + } + } + return views, viewsUsers, nil +}