diff --git a/.github/workflows/fuzz.yaml b/.github/workflows/fuzz.yaml new file mode 100755 index 00000000..7ee6c22a --- /dev/null +++ b/.github/workflows/fuzz.yaml @@ -0,0 +1,17 @@ +name: Fuzzing +on: [push, pull_request] +jobs: + build: + name: Fuzzing + runs-on: ubuntu-latest + steps: + - name: Check out code into the Go module directory + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + - name: Set up Go + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 + with: + go-version-file: "go.mod" + + - name: Fuzz + run: go test ./tests -fuzz . -fuzztime=2m diff --git a/cmd/cmd-run.go b/cmd/cmd-run.go index d4ed2478..3db3324b 100755 --- a/cmd/cmd-run.go +++ b/cmd/cmd-run.go @@ -149,6 +149,13 @@ func run(cmd *cobra.Command, _ []string) error { } } + if maxReviewers < 0 { + return errors.New("max-reviewers cannot be negative") + } + if maxTeamReviewers < 0 { + return errors.New("max-team-reviewers cannot be negative") + } + var regExIncludeRepository *regexp.Regexp var regExExcludeRepository *regexp.Regexp if repoInclude != "" { diff --git a/tests/fuzzing_test.go b/tests/fuzzing_test.go new file mode 100644 index 00000000..768a1cfe --- /dev/null +++ b/tests/fuzzing_test.go @@ -0,0 +1,132 @@ +package tests + +import ( + "fmt" + "os" + "path/filepath" + "testing" + + "github.com/lindell/multi-gitter/cmd" + "github.com/lindell/multi-gitter/tests/vcmock" + "github.com/stretchr/testify/assert" +) + +func FuzzRun(f *testing.F) { + f.Add( + "assignee1,assignee2", // assignees + "commit message", // commit-message + 1, // concurrent + "skip", // conflict-strategy + false, // draft + false, // dry-run + 1, // fetch-depth + false, // fork + "fork-owner", // fork-owner + "go", // git-type + "label1,label2", // labels + "text", // log-format + "info", // log-level + 1, // max-reviewers + 1, // max-team-reviewers + "pr-body", // pr-body + "pr-title", // pr-title + "reviewer1,reviewer2", // reviewers + false, // skip-forks + false, // skip-pr + "should-not-change", // skip-repo + "team-reviewer1,team-reviewer1", // team-reviewers + "topic1,topic2", // topic + ) + f.Fuzz(func( + t *testing.T, + + assignees string, + commitMessage string, + concurrent int, + conflictStrategy string, + draft bool, + dryRun bool, + fetchDepth int, + fork bool, + forkOwner string, + gitType string, + labels string, + logFormat string, + logLevel string, + maxReviewers int, + maxTeamReviewers int, + prBody string, + prTitle string, + reviewers string, + skipForks bool, + skipPr bool, + skipRepo string, + teamReviewers string, + topic string, + ) { + vcMock := &vcmock.VersionController{} + defer vcMock.Clean() + cmd.OverrideVersionController = vcMock + + tmpDir, err := os.MkdirTemp(os.TempDir(), "multi-git-test-run-") + defer os.RemoveAll(tmpDir) + assert.NoError(t, err) + + workingDir, err := os.Getwd() + assert.NoError(t, err) + + changerBinaryPath := normalizePath(filepath.Join(workingDir, changerBinaryPath)) + + changeRepo := createRepo(t, "owner", "should-change", "i like apples") + changeRepo2 := createRepo(t, "owner", "should-change-2", "i like my apple") + noChangeRepo := createRepo(t, "owner", "should-not-change", "i like oranges") + vcMock.AddRepository(changeRepo) + vcMock.AddRepository(changeRepo2) + vcMock.AddRepository(noChangeRepo) + + runOutFile := filepath.Join(tmpDir, "run-out.txt") + runLogFile := filepath.Join(tmpDir, "run-log.txt") + + command := cmd.RootCmd() + command.SetArgs([]string{"run", + "--output", runOutFile, + "--log-file", runLogFile, + "--author-name", "Test Author", + "--author-email", "test@example.com", + "--assignees", assignees, + "--commit-message", commitMessage, + "--concurrent", fmt.Sprint(concurrent), + "--conflict-strategy", conflictStrategy, + fmt.Sprintf("--draft=%t", draft), + fmt.Sprintf("--dry-run=%t", dryRun), + "--fetch-depth", fmt.Sprint(fetchDepth), + fmt.Sprintf("--fork=%t", fork), + "--fork-owner", forkOwner, + "--git-type", gitType, + "--labels", labels, + "--log-format", logFormat, + "--log-level", logLevel, + "--max-reviewers", fmt.Sprint(maxReviewers), + "--max-team-reviewers", fmt.Sprint(maxTeamReviewers), + "--pr-body", prBody, + "--pr-title", prTitle, + "--reviewers", reviewers, + fmt.Sprintf("--skip-forks=%t", skipForks), + fmt.Sprintf("--skip-pr=%t", skipPr), + "--skip-repo", skipRepo, + "--team-reviewers", teamReviewers, + "--topic", topic, + changerBinaryPath, + }) + err = command.Execute() + if err != nil { + assert.NotContains(t, err.Error(), "panic") + } + + // Verify that the output was correct + runOutData, _ := os.ReadFile(runOutFile) + assert.NotContains(t, string(runOutData), "panic") + runLogData, _ := os.ReadFile(runLogFile) + assert.NotContains(t, string(runLogData), "panic") + }) +}